/* i7094_lp.c: IBM 716 line printer simulator | |
Copyright (c) 2003-2012, 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. | |
lpt 716 line printer | |
19-Jan-07 RMS Added UNIT_TEXT flag | |
Internally, the 7094 works only with column binary and is limited to | |
72 columns of data. Each row of the printed line is represented by | |
72b of data (two 36b words). A complete print line consists of 12 rows | |
(24 36b words). | |
The printer can also echo part of what it prints, namely, the digit rows | |
plus the 8+3 and 8+4 combinations. This was intended for verification of | |
check printing. Echoed data is interspersed with output data in the | |
following order: | |
output row 9 to row 1 | |
echo row "8+4" | |
output row 0 | |
echo row "8+3" | |
output row 11 | |
echo row 9 | |
output row 12 | |
echo row 8 to row 1 | |
*/ | |
#include "i7094_defs.h" | |
#define UNIT_V_CONS (UNIT_V_UF + 0) /* print to console */ | |
#define UNIT_CONS (1u << UNIT_V_CONS) | |
#define UNIT_V_BZ (UNIT_V_UF + 1) | |
#define UNIT_V_48 (UNIT_V_UF + 2) | |
#define UNIT_BZ (1 << UNIT_V_BZ) | |
#define UNIT_48 (1 << UNIT_V_48) | |
#define GET_PCHAIN(x) (((x) >> UNIT_V_BZ) & (UNIT_BZ|UNIT_48)) | |
#define LPT_BINLNT 24 /* bin buffer length */ | |
#define LPT_ECHLNT 22 /* echo buffer length */ | |
#define LPT_CHRLNT 80 /* char buffer length */ | |
#define LPS_INIT 0 /* init state */ | |
#define LPS_DATA 1 /* print data state */ | |
#define ECS_DATA 2 /* echo data state */ | |
#define LPS_END 3 /* end state */ | |
#define LPB_9ROW 0 /* bin buf: 9 row */ | |
#define LPB_8ROW 2 /* 8 row */ | |
#define LPB_4ROW 10 /* 4 row */ | |
#define LPB_3ROW 12 /* 3 row */ | |
#define LPB_1ROW 16 /* 1 row */ | |
#define LPB_12ROW 22 /* 12 row */ | |
#define ECB_84ROW 0 /* echo buf: 8-4 row */ | |
#define ECB_83ROW 2 /* 8-3 row */ | |
#define ECB_9ROW 4 /* 9 row */ | |
#define ECHO_F 0100 /* echo map: flag */ | |
#define ECHO_MASK 0037 /* mask */ | |
#define CMD_BIN 1 /* cmd: bcd/bin */ | |
#define CMD_ECHO 2 /* cmd: wrs/rds */ | |
uint32 lpt_sta = 0; /* state */ | |
uint32 lpt_bptr = 0; /* buffer ptr */ | |
uint32 lpt_cmd = 0; /* modes */ | |
uint32 lpt_tstart = 27500; /* timing */ | |
uint32 lpt_tstop = 27500; | |
uint32 lpt_tleft = 150; | |
uint32 lpt_tright = 4000; | |
t_uint64 lpt_chob = 0; | |
uint32 lpt_chob_v = 0; | |
t_uint64 lpt_bbuf[LPT_BINLNT]; /* binary buffer */ | |
t_uint64 lpt_ebuf[LPT_ECHLNT]; /* echo buffer */ | |
/* Echo ordering map */ | |
static const uint8 echo_map[LPT_BINLNT + LPT_ECHLNT] = { | |
0, 1, 2, 3, 4, 5, 6, 7, /* write 9 to 1 */ | |
8, 9, 10, 11, 12, 13, 14, 15, | |
16, 17, | |
0+ECHO_F, 1+ECHO_F, /* echo 8+4 */ | |
18, 19, /* write 0 */ | |
2+ECHO_F, 3+ECHO_F, /* echo 8+3 */ | |
20, 21, /* write 11 */ | |
4+ECHO_F, 5+ECHO_F, /* echo 9 */ | |
22, 23, /* write 12 */ | |
6+ECHO_F, 7+ECHO_F, 8+ECHO_F, 9+ECHO_F, /* echo 8 to 1 */ | |
10+ECHO_F, 11+ECHO_F, 12+ECHO_F, 13+ECHO_F, | |
14+ECHO_F, 15+ECHO_F, 16+ECHO_F, 17+ECHO_F, | |
18+ECHO_F, 19+ECHO_F, 20+ECHO_F, 21+ECHO_F | |
}; | |
extern uint32 ind_ioc; | |
extern t_uint64 bit_masks[36]; | |
extern uint32 col_masks[12]; | |
extern char bcd_to_ascii_a[64]; | |
extern char bcd_to_ascii_h[64]; | |
extern char bcd_to_pca[64]; | |
extern char bcd_to_pch[64]; | |
char *pch_table[4] = { | |
bcd_to_ascii_h, bcd_to_ascii_a, bcd_to_pch, bcd_to_pca, | |
}; | |
t_stat lpt_reset (DEVICE *dptr); | |
t_stat lpt_svc (UNIT *uptr); | |
t_stat lpt_chsel (uint32 ch, uint32 sel, uint32 unit); | |
t_stat lpt_chwr (uint32 ch, t_uint64 val, uint32 flags); | |
t_stat lpt_end_line (UNIT *uptr); | |
extern char colbin_to_bcd (uint32 colbin); | |
/* LPT data structures | |
lpt_dev LPT device descriptor | |
lpt_unit LPT unit descriptor | |
lpt_reg LPT register list | |
*/ | |
DIB lpt_dib = { &lpt_chsel, &lpt_chwr }; | |
UNIT lpt_unit = { | |
UDATA (&lpt_svc, UNIT_SEQ+UNIT_ATTABLE+UNIT_CONS+UNIT_TEXT, 0) | |
}; | |
REG lpt_reg[] = { | |
{ ORDATA (STATE, lpt_sta, 2) }, | |
{ ORDATA (CMD, lpt_cmd, 2) }, | |
{ ORDATA (CHOB, lpt_chob, 36) }, | |
{ FLDATA (CHOBV, lpt_chob_v, 0) }, | |
{ DRDATA (BPTR, lpt_bptr, 6), PV_LEFT }, | |
{ BRDATA (BUF, lpt_bbuf, 8, 36, LPT_BINLNT) }, | |
{ BRDATA (EBUF, lpt_ebuf, 8, 36, LPT_ECHLNT) }, | |
{ DRDATA (POS, lpt_unit.pos, T_ADDR_W), PV_LEFT }, | |
{ DRDATA (TSTART, lpt_tstart, 24), PV_LEFT + REG_NZ }, | |
{ DRDATA (TSTOP, lpt_tstop, 24), PV_LEFT + REG_NZ }, | |
{ DRDATA (TLEFT, lpt_tleft, 24), PV_LEFT + REG_NZ }, | |
{ DRDATA (TRIGHT, lpt_tright, 24), PV_LEFT + REG_NZ }, | |
{ NULL } | |
}; | |
MTAB lpt_mod[] = { | |
{ UNIT_CONS, UNIT_CONS, "default to console", "DEFAULT" }, | |
{ UNIT_CONS, 0 , "no default device", "NODEFAULT" }, | |
{ UNIT_48, UNIT_48, "48 character chain", "48" }, | |
{ UNIT_48, 0, "64 character chain", "64" }, | |
{ UNIT_BZ, UNIT_BZ, "business set", "BUSINESS" }, | |
{ UNIT_BZ, 0, "Fortran set", "FORTRAN" }, | |
{ 0 } | |
}; | |
DEVICE lpt_dev = { | |
"LPT", &lpt_unit, lpt_reg, lpt_mod, | |
1, 10, 31, 1, 8, 7, | |
NULL, NULL, &lpt_reset, | |
NULL, NULL, NULL, | |
&lpt_dib, DEV_DISABLE | |
}; | |
/* Channel select routine */ | |
t_stat lpt_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_RDS: /* read */ | |
case CHSL_WRS: /* write */ | |
if (!(lpt_unit.flags & (UNIT_ATT|UNIT_CONS))) /* not attached? */ | |
return SCPE_UNATT; | |
if (sim_is_active (&lpt_unit)) /* busy? */ | |
return ERR_STALL; | |
lpt_cmd = ((unit & 02)? CMD_BIN: 0) | /* save modes */ | |
((sel == CHSL_RDS)? CMD_ECHO: 0); | |
lpt_sta = LPS_INIT; /* initial state */ | |
sim_activate (&lpt_unit, lpt_tstart); /* start reader */ | |
break; | |
default: /* other */ | |
return STOP_ILLIOP; | |
} | |
return SCPE_OK; | |
} | |
/* Channel write routine | |
- Normal mode is processed here | |
- Echo mode is processed in the service routine (like a read) */ | |
t_stat lpt_chwr (uint32 ch, t_uint64 val, uint32 eorfl) | |
{ | |
uint32 u = (lpt_cmd & CMD_BIN)? U_LPBIN: U_LPBCD; /* reconstruct unit */ | |
lpt_chob = val & DMASK; /* store data */ | |
lpt_chob_v = 1; /* set valid */ | |
if (lpt_sta == ECS_DATA) | |
return SCPE_OK; | |
if (lpt_sta == LPS_DATA) { | |
lpt_bbuf[lpt_bptr++] = lpt_chob; /* store data */ | |
if (eorfl || /* end record, or */ | |
((lpt_cmd & CMD_BIN)? /* last word in buffer? */ | |
(lpt_bptr > (LPB_1ROW + 1)): /* (binary mode) */ | |
(lpt_bptr > (LPB_12ROW + 1)))) { /* (normal mode) */ | |
ch6_set_flags (CH_A, u, CHF_EOR); /* set eor */ | |
return lpt_end_line (&lpt_unit); | |
} | |
return SCPE_OK; | |
} | |
return SCPE_IERR; | |
} | |
/* Unit timeout */ | |
t_stat lpt_svc (UNIT *uptr) | |
{ | |
uint32 u = (lpt_cmd & CMD_BIN)? U_LPBIN: U_LPBCD; /* reconstruct unit */ | |
uint32 i, map; | |
switch (lpt_sta) { /* case on state */ | |
case LPS_INIT: /* initial state */ | |
for (i = 0; i < LPT_BINLNT; i++) /* clear data buffer */ | |
lpt_bbuf[i] = 0; | |
for (i = 0; i < LPT_ECHLNT; i++) /* clear echo buffer */ | |
lpt_ebuf[i] = 0; | |
if (lpt_cmd & CMD_BIN) /* set buffer ptr */ | |
lpt_bptr = LPB_1ROW; | |
else lpt_bptr = LPB_9ROW; | |
if (lpt_cmd & CMD_ECHO) /* set data state */ | |
lpt_sta = ECS_DATA; | |
else lpt_sta = LPS_DATA; | |
ch6_req_wr (CH_A, u); /* request channel */ | |
lpt_chob = 0; /* clr, inval buffer */ | |
lpt_chob_v = 0; | |
sim_activate (uptr, lpt_tleft); /* go again */ | |
break; | |
case LPS_DATA: /* print data state */ | |
if (!ch6_qconn (CH_A, u)) /* disconnect? */ | |
return lpt_end_line (uptr); /* line is done */ | |
if (lpt_chob_v) /* valid? clear */ | |
lpt_chob_v = 0; | |
else ind_ioc = 1; /* no, io check */ | |
ch6_req_wr (CH_A, u); /* request chan again */ | |
sim_activate (uptr, (lpt_bptr & 1)? lpt_tleft: lpt_tright); | |
break; | |
case ECS_DATA: /* echo data state */ | |
map = echo_map[lpt_bptr++]; /* map column */ | |
if (map == ECHO_F) { /* first echo? */ | |
lpt_ebuf[ECB_84ROW] = lpt_bbuf[LPB_8ROW] & lpt_bbuf[LPB_4ROW]; | |
lpt_ebuf[ECB_84ROW + 1] = lpt_bbuf[LPB_8ROW + 1] & lpt_bbuf[LPB_4ROW + 1]; | |
lpt_ebuf[ECB_83ROW] = lpt_bbuf[LPB_8ROW] & lpt_bbuf[LPB_3ROW]; | |
lpt_ebuf[ECB_83ROW + 1] = lpt_bbuf[LPB_8ROW + 1] & lpt_bbuf[LPB_3ROW + 1]; | |
for (i = 0; i < 18; i++) /* copy rows 9.. 1 */ | |
lpt_ebuf[ECB_9ROW + i] = lpt_bbuf[LPB_9ROW + i]; | |
} | |
if (map & ECHO_F) { /* echo cycle */ | |
ch6_req_rd (CH_A, u, lpt_ebuf[map & ECHO_MASK], 0); | |
if (lpt_bptr >= (LPT_BINLNT + LPT_ECHLNT)) | |
return lpt_end_line (uptr); /* done? */ | |
sim_activate (uptr, lpt_tleft); /* short timer */ | |
} | |
else { /* print cycle */ | |
if (lpt_chob_v) /* valid? clear */ | |
lpt_chob_v = 0; | |
else ind_ioc = 1; /* no, io check */ | |
lpt_bbuf[map] = lpt_chob; /* store in buffer */ | |
sim_activate (uptr, (lpt_bptr & 1)? lpt_tleft: lpt_tright); | |
} | |
if (!(echo_map[lpt_bptr] & ECHO_F)) /* print word next? */ | |
ch6_req_wr (CH_A, u); /* req channel */ | |
break; | |
case LPS_END: /* end state */ | |
if (ch6_qconn (CH_A, u)) { /* lpt still conn? */ | |
lpt_sta = LPS_INIT; /* initial state */ | |
sim_activate (uptr, 1); /* next line */ | |
} | |
break; | |
} | |
return SCPE_OK; | |
} | |
/* End line routine */ | |
t_stat lpt_end_line (UNIT *uptr) | |
{ | |
uint32 i, col, row, bufw, colbin; | |
char *pch, bcd, lpt_cbuf[LPT_CHRLNT + 1]; | |
t_uint64 dat; | |
pch = pch_table[GET_PCHAIN (lpt_unit.flags)]; /* get print chain */ | |
for (col = 0; col < (LPT_CHRLNT + 1); col++) /* clear ascii buf */ | |
lpt_cbuf[col] = ' '; | |
for (col = 0; col < 72; col++) { /* proc 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 (lpt_bbuf[bufw] & dat) | |
colbin |= col_masks[row]; | |
} | |
bcd = colbin_to_bcd (colbin); /* column bin -> BCD */ | |
lpt_cbuf[col] = pch[bcd]; /* -> ASCII */ | |
} | |
for (i = LPT_CHRLNT; (i > 0) && | |
(lpt_cbuf[i - 1] == ' '); --i) ; /* trim spaces */ | |
lpt_cbuf[i] = 0; /* append nul */ | |
if (uptr->flags & UNIT_ATT) { /* file? */ | |
fputs (lpt_cbuf, uptr->fileref); /* write line */ | |
fputc ('\n', uptr->fileref); /* append nl */ | |
uptr->pos = ftell (uptr->fileref); /* update position */ | |
if (ferror (uptr->fileref)) { /* error? */ | |
perror ("LPT I/O error"); | |
clearerr (uptr->fileref); | |
return SCPE_IOERR; | |
} | |
} | |
else if (uptr->flags & UNIT_CONS) { /* print to console? */ | |
for (i = 0; lpt_cbuf[i] != 0; i++) | |
sim_putchar (lpt_cbuf[i]); | |
sim_putchar ('\r'); | |
sim_putchar ('\n'); | |
} | |
else return SCPE_UNATT; /* otherwise error */ | |
lpt_sta = LPS_END; /* end line state */ | |
sim_cancel (uptr); /* cancel current */ | |
sim_activate (uptr, lpt_tstop); /* long timer */ | |
return SCPE_OK; | |
} | |
/* Reset routine */ | |
t_stat lpt_reset (DEVICE *dptr) | |
{ | |
uint32 i; | |
for (i = 0; i < LPT_BINLNT; i++) /* clear bin buf */ | |
lpt_bbuf[i] = 0; | |
for (i = 0; i < LPT_ECHLNT; i++) /* clear echo buf */ | |
lpt_ebuf[i] = 0; | |
lpt_sta = 0; /* clear state */ | |
lpt_cmd = 0; /* clear modes */ | |
lpt_bptr = 0; /* clear buf ptr */ | |
lpt_chob = 0; | |
lpt_chob_v = 0; | |
sim_cancel (&lpt_unit); /* stop printer */ | |
return SCPE_OK; | |
} |