/* i650_cdp.c: IBM 650 Card punch. | |
Copyright (c) 2018, Roberto Sancho | |
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 | |
ROBERTO SANCHO 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. | |
This is the standard card punch. | |
These units each buffer one record in local memory and signal | |
ready when the buffer is full or empty. The channel must be | |
ready to recieve/transmit data when they are activated since | |
they will transfer their block during chan_cmd. All data is | |
transmitted as BCD characters. | |
*/ | |
#include "i650_defs.h" | |
#include "sim_card.h" | |
#define UNIT_CDP UNIT_ATTABLE | MODE_026 | MODE_LOWER | |
/* std devices. data structures | |
cdp_dev Card Punch device descriptor | |
cdp_unit Card Punch unit descriptor | |
cdp_reg Card Punch register list | |
cdp_mod Card Punch modifiers list | |
*/ | |
uint32 cdp_cmd(UNIT *, uint16, uint16); | |
t_stat cdp_srv(UNIT *); | |
t_stat cdp_reset(DEVICE *); | |
t_stat cdp_attach(UNIT *, CONST char *); | |
t_stat cdp_detach(UNIT *); | |
t_stat cdp_help(FILE *, DEVICE *, UNIT *, int32, const char *); | |
const char *cdp_description(DEVICE *dptr); | |
t_stat cdp_set_wiring (UNIT *uptr, int32 val, CONST char *cptr, void *desc); | |
t_stat cdp_show_wiring (FILE *st, UNIT *uptr, int32 val, CONST void *desc); | |
t_stat cdp_set_echo (UNIT *uptr, int32 val, CONST char *cptr, void *desc); | |
t_stat cdp_show_echo (FILE *st, UNIT *uptr, int32 val, CONST void *desc); | |
UNIT cdp_unit[4] = { | |
{UDATA(cdp_srv, UNIT_CDP, 0), 600}, // unit 0 is the printing mechanism of 407 | |
{UDATA(cdp_srv, UNIT_CDP, 0), 600}, | |
{UDATA(cdp_srv, UNIT_CDP, 0), 600}, | |
{UDATA(cdp_srv, UNIT_CDP, 0), 600}, | |
}; | |
MTAB cdp_mod[] = { | |
{MTAB_XTD | MTAB_VUN, 0, "FORMAT", "FORMAT", &sim_card_set_fmt, &sim_card_show_fmt, NULL, "Set card format"}, | |
{MTAB_XTD | MTAB_VUN, 0, "WIRING", "WIRING", &cdp_set_wiring, &cdp_show_wiring, NULL, "Set card punch/print control panel Wiring"}, | |
{MTAB_XTD | MTAB_VUN, 0, "ECHO", "ECHO", &cdp_set_echo, &cdp_show_echo, NULL, "Set console printout for punched cards"}, | |
{MTAB_XTD | MTAB_VUN, 1, "PRINT", "PRINT", &cdp_set_echo, &cdp_show_echo, NULL, "Set printout on CDP0 unit for punched cards"}, | |
{0} | |
}; | |
DEVICE cdp_dev = { | |
"CDP", cdp_unit, NULL, cdp_mod, | |
4, 8, 15, 1, 8, 8, | |
NULL, NULL, NULL, NULL, &cdp_attach, &cdp_detach, | |
&cdp_dib, DEV_DISABLE | DEV_DEBUG, 0, crd_debug, | |
NULL, NULL, &cdp_help, NULL, NULL, &cdp_description | |
}; | |
// vars where card is encoded for punching | |
char card_buf[120]; | |
int card_nbuf; | |
// vars where card is encoded for printing | |
char card_lpt[120]; | |
int card_nlpt; | |
void encode_char(int cPunch, int cLpt) | |
{ | |
if ((cPunch) && (card_nbuf < 80)) { | |
card_buf[card_nbuf++] = cPunch; | |
} | |
if ((cLpt) && (card_nlpt < 120)) { | |
card_lpt[card_nlpt++] = cLpt; | |
} | |
} | |
void encode_lpt_spc(int nSpaces) | |
{ | |
while (nSpaces-- >0) encode_char(0, 32); | |
} | |
void encode_lpt_str(const char * buf) | |
{ | |
while (*buf) encode_char(0, *buf++); | |
} | |
void encode_lpt_num(t_int64 d, int l) | |
{ | |
char s[20]; | |
int i,n; | |
char pad; | |
if (l < 0) { | |
l=-l; pad = ' '; // if l < 0 pad with space | |
} else { | |
pad = '0'; // if l > 0 pag with zero | |
} | |
d=AbsWord(d); | |
for (i=9;i>=0;i--) { | |
n = (int) (d % 10); | |
d = d / 10; | |
s[i] = '0' + n; | |
} | |
s[10] = 0; | |
if (pad == ' ') { | |
for(i=0;i<9;i++) { | |
if (s[i] != '0') break; | |
s[i] = ' '; | |
} | |
} | |
encode_lpt_str(&s[10-l]); | |
} | |
#define wf_NNNNNNNNNNs 0 | |
#define wf_NN_NNNN_NNNNs 1 | |
#define wf_sN_NNNNNNN_NN 3 | |
#define wf_sN_NNN_NNN_NNN 4 | |
#define wf_nnnnnnnnnNs 5 | |
void encode_lpt_word(t_int64 d, int NegZero, int wFormat) | |
{ | |
int n; | |
int neg=0; | |
if (d < 0) {d=-d; neg=1;} else if ((d==0) && (NegZero)) neg=1; | |
if (wFormat == wf_NN_NNNN_NNNNs) { | |
n = Shift_Digits(&d, 2); encode_lpt_num(n, 2); encode_lpt_spc(1); | |
n = Shift_Digits(&d, 4); encode_lpt_num(n, 4); encode_lpt_spc(1); | |
n = Shift_Digits(&d, 4); encode_lpt_num(n, 4); | |
encode_char(0, neg ? '-':' '); | |
} else if (wFormat == wf_sN_NNNNNNN_NN) { | |
encode_char(0, neg ? '-':'+'); | |
n = Shift_Digits(&d, 1); encode_lpt_num(n, 1); encode_lpt_spc(1); | |
n = Shift_Digits(&d, 7); encode_lpt_num(n, 7); encode_lpt_spc(1); | |
n = Shift_Digits(&d, 2); encode_lpt_num(n, 2); | |
} else if (wFormat == wf_sN_NNN_NNN_NNN) { | |
encode_char(0, neg ? '-':'+'); | |
n = Shift_Digits(&d, 1); encode_lpt_num(n, 1); encode_lpt_spc(1); | |
n = Shift_Digits(&d, 3); encode_lpt_num(n, 3); encode_lpt_spc(1); | |
n = Shift_Digits(&d, 3); encode_lpt_num(n, 3); encode_lpt_spc(1); | |
n = Shift_Digits(&d, 3); encode_lpt_num(n, 3); | |
} else if (wFormat == wf_nnnnnnnnnNs) { | |
encode_lpt_num(d,-10); // replace leading zeroes by spaces | |
encode_char(0, neg ? '-':' '); | |
} else { // default: wFormat == wf_NNNNNNNNNNs | |
encode_lpt_num(d,10); | |
encode_char(0, neg ? '-':' '); | |
} | |
} | |
// set pch_word[10] with encoded word d. | |
// if d negative, sign on last digit (units digit) | |
// if bSetHiPuch=1, set HiPunch on last digit. | |
// if bSetHiPuch=2, set HiPunch on last digit and on second digit. | |
// if bSetHiPuch=3, set HiPunch on third digit | |
// if last digit is negative, never set HiPunch even if asked for (a card column cannot have both X(11) and Y(12) punched) | |
void sprintf_word(char * pch_word, t_int64 d, int NegZero, int bSetHiPuch) | |
{ | |
int i,n,neg, hi; | |
if (d < 0) { | |
neg = 1; | |
d = -d; | |
} else if ((d == 0) && (NegZero)) { | |
neg = 1; // Negative Zero -> also puncho X(11) on last 0 digit | |
} else { | |
neg = 0; | |
} | |
for (i=9;i>=0;i--) { | |
hi = 0; | |
if ((i==1) && (bSetHiPuch == 2)) hi = 1; // Set Hi Punch on second digit | |
if ((i==2) && (bSetHiPuch == 3)) hi = 1; // Set Hi Punch on third digit | |
if ((i==9) && ( (bSetHiPuch == 1) || (bSetHiPuch == 2) ) && (neg == 0)) hi = 1; // Set Hi Punch on last digit (units digit) | |
n = (int) (d % 10); | |
d = d / 10; | |
n = n + hi * 10; | |
if ((neg == 1) && (i==9)) n = n + 20; // Set negative punch X(11) on last digit | |
pch_word[i] = digits_ascii[n]; | |
} | |
pch_word[10] = 0; | |
} | |
void encode_pch_str(const char * buf) | |
{ | |
while (*buf) { | |
encode_char(*buf++, 0); | |
} | |
} | |
void encode_8word_wiring(int addr) | |
{ | |
// encode 8 numerical words per card | |
// get the decoded data from drum at addr | |
int i, NegZero; | |
t_int64 d; | |
char pch_word[20]; | |
// punch card | |
for(i=0;i<8;i++) { | |
ReadDrum(addr + i, &d, &NegZero); | |
sprintf_word(pch_word, d, NegZero, 0); | |
encode_pch_str(pch_word); | |
} | |
// print out card contents | |
// 8 words in format NN NNNN NNNN+ | |
for(i=0;i<8;i++) { | |
ReadDrum(addr + i, &d, &NegZero); | |
encode_lpt_word(d, NegZero, wf_NN_NNNN_NNNNs); | |
encode_lpt_spc(1); | |
} | |
} | |
void encode_soap_wiring(int addr) | |
{ | |
// encode soap card simulating soap control panel wiring for 533 | |
// from SOAP II manual at http://www.bitsavers.org/pdf/ibm/650/24-4000-0_SOAPII.pdf | |
// storage in output block | |
// +-------------------+ | |
// Word 1977: | <- Location -> | Alphabetic | |
// 1978: | <- Data Addr -> | Alphabetic | |
// 1979: | <- Inst Addr -> | Alphabetic | |
// +-+-+-|-+-+-|-+-|-+-| | |
// 1980: | Op Code |DTg|ITg| Alphabetic | |
// +-+-+-|-+-+-|-+-|-+-| | |
// 1981: | <- Remarks -> | Alphabetic | |
// 1982: | <- Remarks -> | Alphabetic | |
// 1983: |<-Assembled Instr->| | |
// +-+-|-+-+-+-|-+-+-|-| | |
// 1984: | |N N N N| |T| N N N N=Location, T=Type (0 if Blank) | |
// 1985: | |N N N N| N N N N=Card Number | |
// 1986: |a|b|c|d|e|f|g|h|i|j| a = 0/8 (for non blank type) | |
// b = 0/8 (negative) | |
// c = 0/8 (bypass) | |
// d = 0/8 (punch a) =8 -> do not print Loc op da ir | |
// e = 0/8 (punch b) =8 -> punch availability table | |
// f = 0/8 (800X instruction) | |
// g = 0/8 (blank out L) | |
// h = 0/8 (blank out D) | |
// i = 0/8 (blank out I) | |
// j = 0/8 (blank out OP) | |
// | |
// SOAP printout format | |
// | Sg | Location | OpCode | Data Addr | Tg | Instr Addr | Tg | Remarks | Drum Addr | NN NNNN NNNN[-] (signed word value at this drum addr) | |
// SOAP punch format (load card, 1 word per card) | |
// simulates punching over prepunched 1-word load card | |
// | word1 | nnnn | 24 addr 800? | NNNNNNNNNN[-] | source soap line | |
// nnnn=card number | |
// addr=drum address where the word is loaded | |
// NNNNNNNNNN=word to be loaded at addr, with sign | |
char loc[6], data_addr[6], inst_addr[6], OpCode[6], Data_Tag[6], Instr_Tag[6], rem1[6], rem2[6]; | |
char pch_word[20]; | |
t_int64 d, instr; | |
int location, CardNum, ty; | |
int b_non_blank, neg, b_blk_op, b_blk_i, b_blk_d, b_blk_l, b_800X, b_pch_b, b_pch_a, b_bypass; // punch control flags | |
int i, sv_card_nbuf, n; | |
int pat1, pat2; | |
word_to_ascii(loc, 1, 5, DRUM[addr + 0]); | |
word_to_ascii(data_addr, 1, 5, DRUM[addr + 1]); | |
word_to_ascii(inst_addr, 1, 5, DRUM[addr + 2]); | |
word_to_ascii(OpCode, 1, 3, DRUM[addr + 3]); | |
word_to_ascii(Data_Tag, 4, 1, DRUM[addr + 3]); | |
word_to_ascii(Instr_Tag, 5, 1, DRUM[addr + 3]); | |
word_to_ascii(rem1, 1, 5, DRUM[addr + 4]); | |
word_to_ascii(rem2, 1, 5, DRUM[addr + 5]); | |
instr = DRUM[addr + 6]; | |
location = (int) ((DRUM[addr + 7] / D4) % D4); | |
ty = (int) ( DRUM[addr + 7] % 10); | |
CardNum = (int) ( DRUM[addr + 8] % D4); | |
d = DRUM[addr + 9]; | |
b_blk_op = ((int) (d % 10) == 8) ? 1:0; d = d / 10; | |
b_blk_i = ((int) (d % 10) == 8) ? 1:0; d = d / 10; | |
b_blk_d = ((int) (d % 10) == 8) ? 1:0; d = d / 10; | |
b_blk_l = ((int) (d % 10) == 8) ? 1:0; d = d / 10; | |
b_800X = ((int) (d % 10) == 8) ? 1:0; d = d / 10; | |
b_pch_b = ((int) (d % 10) == 8) ? 1:0; d = d / 10; | |
b_pch_a = ((int) (d % 10) == 8) ? 1:0; d = d / 10; | |
b_bypass = ((int) (d % 10) == 8) ? 1:0; d = d / 10; | |
neg = ((int) (d % 10) == 8) ? 1:0; d = d / 10; | |
b_non_blank = ((int) (d % 10) == 8) ? 1:0; d = d / 10; | |
// printf("bits %06d%04d%c ", printfw(DRUM[addr + 9])); // to echo the status digits of punched card | |
// generate card | |
if (b_pch_b) { | |
// punch availability table (pat pseudo-op output) | |
for(i=0;i<8;i++) { | |
sprintf_word(pch_word, DRUM[addr + i], 0, 1); | |
encode_pch_str(pch_word); | |
} | |
} else { | |
if (b_pch_a) { | |
// punch non generating code card | |
encode_pch_str("0?0000800?"); // load card | |
sprintf(pch_word, " %04d", CardNum); // card number | |
encode_pch_str(pch_word); | |
encode_pch_str(" "); // two blank words | |
encode_pch_str(" "); | |
if (b_non_blank) encode_pch_str("1"); else encode_pch_str(" "); | |
} else { | |
// punch generating code card | |
if (b_800X) { | |
encode_pch_str("6I1954800?"); // load card for word to be stored in 800X addr | |
} else { | |
encode_pch_str("6I1954195C"); // load card for word to be stored in drum | |
} | |
sprintf(pch_word, " %04d", CardNum); // card number | |
encode_pch_str(pch_word); | |
sprintf(pch_word, "24%04d800?", location);// addr to place the loaded word | |
encode_pch_str(pch_word); | |
sprintf_word(pch_word, AbsWord(instr) * (neg ? -1:1), ((neg) && (instr == 0)) ? 1:0, 1); | |
encode_pch_str(pch_word); | |
encode_char(ty == 0 ? ' ' : '0'+ty, 0); | |
} | |
encode_pch_str(" "); | |
sv_card_nbuf = card_nbuf; // save pch bufer current pos | |
encode_pch_str(loc); encode_pch_str(OpCode); | |
encode_pch_str(data_addr); encode_pch_str(Data_Tag); | |
encode_pch_str(inst_addr); encode_pch_str(Instr_Tag); | |
encode_pch_str(rem1); encode_pch_str(rem2); | |
// convert to lowercase for punching | |
for (i=sv_card_nbuf;i<card_nbuf;i++) | |
if ((card_buf[i] >= 'A') && (card_buf[i] <= 'Z')) | |
card_buf[i] = card_buf[i] - 'A' + 'a'; | |
card_buf[card_nbuf] = 0; | |
} | |
// generate printout | |
if (b_pch_b) { | |
// print availability table (pat pseudo-op output) | |
for(i=0; i<4; i++) { | |
d = DRUM[addr + i*2]; | |
pat1 = (int) ((d / D4) % D4); | |
pat2 = (int) ( d % D4); | |
d = DRUM[addr + i*2 + 1]; | |
encode_lpt_num(pat1, 4); | |
encode_lpt_spc(2); | |
encode_lpt_num(d, 10); | |
encode_lpt_spc(2); | |
encode_lpt_num(pat2, 4); | |
encode_lpt_spc(5); | |
} | |
} else if ((ty == 1) || (ty == 5)) { | |
// print coment for card type 1 (SOAP II) or type 5 (SOAP modified for IT) | |
encode_char(0, '0' + ty); | |
encode_lpt_spc(14); | |
encode_lpt_str(loc); encode_lpt_str(OpCode); | |
encode_lpt_str(data_addr); encode_lpt_str(Data_Tag); | |
encode_lpt_str(inst_addr); encode_lpt_str(Instr_Tag); | |
encode_lpt_str(rem1); encode_lpt_str(rem2); | |
} else { | |
if (ty == 0) { | |
encode_lpt_spc(1); | |
} else { | |
encode_char(0, '0' + ty); | |
} | |
encode_lpt_str(loc); | |
encode_lpt_spc(2); encode_char(0, neg ? '-':' '); encode_lpt_spc(1); | |
encode_lpt_str(OpCode); encode_lpt_spc(3); | |
encode_lpt_str(data_addr); encode_lpt_str(Data_Tag); encode_lpt_spc(2); | |
encode_lpt_str(inst_addr); encode_lpt_str(Instr_Tag); encode_lpt_spc(5); | |
encode_lpt_str(rem1); encode_lpt_str(rem2); | |
if (b_pch_a) { | |
// blank op -> do not print location and intruction | |
if (b_bypass) { | |
encode_lpt_spc(4); | |
encode_lpt_str("BYPASS"); | |
} | |
} else { | |
encode_lpt_spc(4); | |
if (b_blk_l) { encode_lpt_spc(4); } else encode_lpt_num(location, 4); | |
encode_lpt_spc(2); encode_char(0, neg ? '-':' '); encode_lpt_spc(1); | |
d = instr; | |
n = Shift_Digits(&d, 2); // operation code (2 digits) | |
if (b_blk_op) { encode_lpt_spc(2); } else encode_lpt_num(n, 2); | |
encode_lpt_spc(2); | |
n = Shift_Digits(&d, 4); // data addr (4 digits) | |
if (b_blk_d) { encode_lpt_spc(4); } else encode_lpt_num(n, 4); | |
encode_lpt_spc(2); | |
n = Shift_Digits(&d, 4); // instr addr (4 digits) | |
if (b_blk_i) { encode_lpt_spc(4); } else encode_lpt_num(n, 4); | |
encode_lpt_spc(1); | |
if (b_blk_l) encode_lpt_str("BLANK L"); else | |
if (b_blk_op) encode_lpt_str("BLANK OP"); else | |
if (b_blk_d) encode_lpt_str("BLANK D"); else | |
if (b_blk_i) encode_lpt_str("BLANK I"); | |
} | |
} | |
} | |
void encode_is_wiring(int addr) | |
{ | |
// encode Floationg Decimal Interpretive System (IS) card simulating control panel wiring for 533 as described | |
// in manual at http://www.bitsavers.org/pdf/ibm/650/28-4024_FltDecIntrpSys | |
// storage in output block | |
// +-+-+-+-+-+-|-+-+-+-| | |
// Word 1977: |Trc|N N N N| | Location | |
// 1978: | |N N N N| | Word Count | |
// +-------------------+ | |
// 1979: | word1 | | |
// 1980: | word2 | | |
// 1981: | word3 | | |
// 1982: | word4 | | |
// 1983: | word5 | | |
// 1984: | word6 | | |
// +-------------------+ | |
// 1985: | Problem Number | | |
// 1986: | |N N N N| | Card Number | |
// +-------------------+ | |
// | |
// if word at 1977 is negative, a load card is punched, but no printout is generated | |
// if word at 1977 is positive, regular output card format is used on punch | |
// Column: 1 2 3 4 | 5 6 | 7 8 9 | 10 | 11 | 12 - 21 | 22 | 23 - 32 | 33 | 34 - 43 | 44 | 45 - 54 | 55 | 56 - 65 | 66 | 67 - 76 | 77 78 79 | 80 | |
// Card | | | Location | wc | s1 | Word1 | s2 | Word2 | s3 | Word3 | s4 | Word4 | s5 | Word5 | s6 | Word6 | Problem | | |
// Num | if location is > 9999, will use column 6 Num | |
// wordN is printed as +N NNNNNNN NN (IT sci notation) | |
// | |
// IT printout format for non tracing cards: | |
// | Location | Word1 | Word2 | Word3 | Word4 | Word5 | Word6 | |
// wordN is printed as +N NNNNNNN NN (IT sci notation) | |
// | |
// IT printout format for tracing cards (Trc digits in word 1977 are non-zero): | |
// | Location | Word1 | Word2 | Word3 | Word4 | Word5 | Word6 | |
// word1 to 3 are printed as +N NNN NNN NNN (IT instruction format) | |
// word4 to 6 are printed as +N NNNNNNN NN (IT sci notation) | |
// | |
int i, NegZero; | |
t_int64 d; | |
int CardNum, loc, wc, PrNum, bTraceCard; | |
char pch_word[20]; | |
int bSetHiPunch; | |
bSetHiPunch = (DRUM[addr] < 0) ? 2 : 0; // first bSetHiPunch is 2 if word negative (signals a load card must be punched) | |
loc = (int) ((DRUM[addr] / D4) % D4); | |
CardNum = (int) ((DRUM[addr+9] / D4) % D4); | |
wc = (int) ((DRUM[addr+1] / D4) % D4); | |
PrNum = (int) ( DRUM[addr+8]); | |
bTraceCard = (DRUM[addr] / D8) > 0 ? 1 : 0; // if to higher digits are nonzero -> is a trace card | |
if (bSetHiPunch) { | |
// punch a load card | |
for(i=0;i<8;i++) { | |
ReadDrum(addr + i, &d, &NegZero); | |
if ((i==0) && (d < 0)) d = -d; // get absolute value for DRUM[addr + 0] | |
sprintf_word(pch_word, d, NegZero, bSetHiPunch); | |
if (bSetHiPunch==2) bSetHiPunch = 1; // if bSetHiPunch is 2 change it to bSetHiPunch = 1 | |
encode_pch_str(pch_word); | |
} | |
} else { | |
// punch a card using output format | |
if (loc < 1000) { | |
sprintf(pch_word, "%04d %03d%01d", CardNum, loc, wc); | |
} else { | |
sprintf(pch_word, "%04d %04d%01d", CardNum, loc, wc); | |
} | |
encode_pch_str(pch_word); | |
for(i=0;i<6;i++) { | |
if (i<wc) { | |
ReadDrum(addr + i + 2, &d, &NegZero); | |
if ((d < 0) || ((d==0) && (NegZero))) { | |
encode_pch_str("-"); | |
d = -d; | |
} else { | |
encode_pch_str("+"); | |
} | |
sprintf_word(pch_word, d, 0, 0); | |
encode_pch_str(pch_word); | |
} else { | |
encode_pch_str(" "); // 11 spaces | |
} | |
} | |
if (PrNum < 0) PrNum = 0; | |
if (PrNum > 999) PrNum = 999; | |
sprintf(pch_word, "%03d", PrNum); | |
encode_pch_str(pch_word); | |
} | |
if (bSetHiPunch) { | |
// load card, does not generate printout | |
// mark lpt output buffer to not print | |
if (card_nlpt == 0) { | |
card_lpt[card_nlpt++] = 0; | |
} | |
} else { | |
// not load card -> do normal printout for card | |
if (wc > 6) wc = 6; | |
if (loc < 1000) { | |
encode_lpt_spc(1); | |
encode_lpt_num(loc, 3); | |
} else { | |
encode_lpt_num(loc, 4); | |
} | |
for(i=2;i<2+wc;i++) { | |
encode_lpt_spc(2); | |
ReadDrum(addr + i, &d, &NegZero); | |
if ((bTraceCard) && (i<5)) { | |
// if printing a trace card, first three words are printed as intructions (+N NNN NNN NNN) | |
encode_lpt_word(d, NegZero, wf_sN_NNN_NNN_NNN); | |
} else { | |
// print numbers adding spaces to ease reading IT floating point format (+N NNNNNNN NN) | |
encode_lpt_word(d, NegZero, wf_sN_NNNNNNN_NN); | |
} | |
} | |
} | |
} | |
void encode_it_wiring(int addr) | |
{ | |
// encode card for IT compiler modified soap | |
// from IT manual at http://www.bitsavers.org/pdf/ibm/650/CarnegieInternalTranslator.pdf | |
// storage in output block | |
// +-------------------+ | |
// Word 1977: | <- Loc. Label -> | Alphabetic | |
// 1978: | <- Op Code -> | Alphabetic | |
// 1979: | <- Data Addr -> | Alphabetic | |
// 1980: | <- Inst Addr -> | Alphabetic | |
// 1981: | <- Remarks -> | Alphabetic | |
// 1982: | <- Remarks -> | Alphabetic | |
// +-------------------+ | |
// 1983: | | Not Used | |
// 1984: | | Not Used | |
// +-------------------+ | |
// 1985: | |N N N N| | N N N N=Card Number | |
// 1986: |a|b|c|d|e|f|g|h|i|j| a = 0/8 =8 -> reservation card | |
// b = 0/8 (regional setting) =0 -> card type 3, =8 -> card type 4 | |
// c = 0/8 | |
// d = 0/8 =8 -> negative value | |
// e = 0/8 | |
// f = 0/8 | |
// g = 0/8 =8 -> punching a PIT card | |
// h = 0/8 =8 -> type 1 data out format | |
// i = 0/8 | |
// j = 0/8 | |
// | |
// SIT printout format | |
// | Card Num | Ty | Location | Sg | OpCode | Data Addr | Instr Addr | Remarks | |
// SIT punch format is SOAP source card format | |
// Column: 41 | 42 | 43 44 45 46 47 | 48 49 50 | 51 52 53 54 55 | 56 | 57 58 59 60 61 | 62 | 63 64 65 66 67 68 69 70 71 72 | |
// Ty | Sg | Location | OpCode | Data Addr | | Instr Addr | | Remarks | |
// | |
// Ty = Type = blank, 3 or 4 (regional setting) | |
// Sg = sign = blank or - | |
// | |
// If word 1986 contains 8 in digit h, it is a type 1 data out card format | |
// +----+------+-------+ | |
// Word 1977: | VV | +NNN | SSSS | IT variable 1 | |
// 1978: | Word | | |
// +-------------------+ | |
// 1979: | | IT variable 2 (zero if none) | |
// 1980: | | | |
// +-------------------+ | |
// 1981: | | IT variable 3 | |
// 1982: | | | |
// +-------------------+ | |
// 1983: | | IT variable 4 | |
// 1984: | | | |
// +-------------------+ | |
// 1985: | | Not used | |
// 1986: |8|0|0|0|0|0|8|8|0|0| control word for type 1 data out card | |
// | |
// VV = IT variable being punched: 01 -> I type, 02 -> Y type, 03 -> C type | |
// + N N N = variable number (I5 -> 01 0005). + means zoro with Y(12) overpunch | |
// S S S S = statement number of IT source program where TYPE command that generates the card is | |
// Word = value from IT variable. If type I, is an integer. If type C or Y | |
// type is word is float (M MMMMMMM EE -> M=mantisa, EE=exponent) | |
// can be is negative (X(11) overpunch in last digit) | |
// up to 4 pairs var-word per card | |
// leading zeroes of each word are replaced by spaces | |
char pch_word[20]; | |
char loc[6], data_addr[6], inst_addr[6], OpCode[6], rem1[6], rem2[6]; | |
t_int64 d; | |
int CardNum, ty; | |
int b, neg, b_pit, b_reg, b_resv, b_data; // punch control flags | |
int i; | |
word_to_ascii(loc, 1, 5, DRUM[addr + 0]); | |
word_to_ascii(OpCode, 1, 3, DRUM[addr + 1]); | |
word_to_ascii(data_addr, 1, 5, DRUM[addr + 2]); | |
word_to_ascii(inst_addr, 1, 5, DRUM[addr + 3]); | |
word_to_ascii(rem1, 1, 5, DRUM[addr + 4]); | |
word_to_ascii(rem2, 1, 5, DRUM[addr + 5]); | |
CardNum = (int) ((DRUM[addr + 8] / D4) % D4); | |
d = DRUM[addr + 9]; | |
b = ((int) (d % 10) == 8) ? 1:0; d = d / 10; | |
b = ((int) (d % 10) == 8) ? 1:0; d = d / 10; | |
b_data = ((int) (d % 10) == 8) ? 1:0; d = d / 10; | |
b_pit = ((int) (d % 10) == 8) ? 1:0; d = d / 10; | |
b = ((int) (d % 10) == 8) ? 1:0; d = d / 10; | |
b = ((int) (d % 10) == 8) ? 1:0; d = d / 10; | |
neg = ((int) (d % 10) == 8) ? 1:0; d = d / 10; | |
b = ((int) (d % 10) == 8) ? 1:0; d = d / 10; | |
b_reg = ((int) (d % 10) == 8) ? 1:0; d = d / 10; | |
b_resv = ((int) (d % 10) == 8) ? 1:0; d = d / 10; | |
// printf("bits %06d%04d%c ", printfw(DRUM[addr + 9])); // to echo the status digits of punched card | |
// generate card | |
if (b_data) { | |
// punch type 1 data out card | |
for (i=0;i<4;i++) { | |
sprintf_word(pch_word, DRUM[addr + i*2+0], 0, (i==0) ? 3:0); // punch variable name | |
encode_pch_str(pch_word); | |
sprintf_word(pch_word, DRUM[addr + i*2+1], 0, (i==0) ? 3:0); // punch variable value | |
encode_pch_str(pch_word); | |
if (DRUM[addr + i*2+2] == 0) break; // if next word is zero, no more variables to punch | |
} | |
} else { | |
// punch SOAP source instruction | |
for(i=0;i<40;i++) encode_pch_str(" "); // leave 40 first columns blank | |
if (b_resv) { | |
if (b_reg) { | |
ty = 4; | |
} else { | |
ty = 3; | |
} | |
} else { | |
ty = 0; | |
} | |
encode_char(ty == 0 ? ' ' : '0'+ty, 0); | |
encode_char(neg == 0 ? ' ' : '-', 0); | |
encode_pch_str(loc); | |
encode_pch_str(OpCode); | |
encode_pch_str(data_addr); | |
encode_pch_str(" "); | |
encode_pch_str(inst_addr); | |
encode_pch_str(" "); | |
encode_pch_str(rem1); | |
encode_pch_str(rem2); | |
// convert to lowercase for punching | |
for (i=40;i<card_nbuf;i++) | |
if ((card_buf[i] >= 'A') && (card_buf[i] <= 'Z')) | |
card_buf[i] = card_buf[i] - 'A' + 'a'; | |
card_buf[card_nbuf] = 0; | |
} | |
// generate printout | |
if (b_data) { | |
// print type 1 data out card. replace leading zeroes by spaces on each word | |
for (i=0;i<4;i++) { | |
encode_lpt_word(DRUM[addr + i*2+0], 0, wf_nnnnnnnnnNs); // print variable name | |
encode_lpt_spc(1); | |
encode_lpt_word(DRUM[addr + i*2+1], 0, wf_nnnnnnnnnNs); // print variable value | |
encode_lpt_spc(1); | |
if (DRUM[addr + i*2+2] == 0) break; // if next word is zero, no more variables to punch | |
} | |
} else { | |
// print generated soap source listing | |
encode_lpt_spc(2); | |
encode_lpt_num(CardNum, -4); | |
encode_lpt_spc(2); | |
encode_char(0, ty == 0 ? ' ' : '0'+ty); | |
encode_lpt_spc(2); | |
encode_lpt_str(loc); | |
encode_lpt_spc(2); encode_char(0, neg ? '-':' '); encode_lpt_spc(1); | |
encode_lpt_str(OpCode); encode_lpt_spc(3); | |
encode_lpt_str(data_addr); encode_lpt_spc(1); encode_lpt_spc(2); | |
encode_lpt_str(inst_addr); encode_lpt_spc(6); | |
encode_lpt_str(rem1); encode_lpt_str(rem2); | |
} | |
} | |
/* Card punch routine */ | |
uint32 cdp_cmd(UNIT * uptr, uint16 cmd, uint16 addr) | |
{ | |
int i,c,h; | |
struct _card_data *data; | |
uint32 wiring; | |
/* Are we currently tranfering? */ | |
if (uptr->u5 & URCSTA_BUSY) | |
return SCPE_BUSY; | |
/* Test ready */ | |
if ((uptr->flags & UNIT_ATT) == 0) { | |
sim_debug(DEBUG_CMD, &cdp_dev, "No cards (no file attached)\n"); | |
return SCPE_NOCARDS; | |
} | |
// copy and translate drum memory words to chars to punch | |
// using the control panel wiring. | |
wiring = (uptr->flags & UNIT_CARD_WIRING); | |
card_nbuf = card_nlpt = 0; | |
if (wiring == WIRING_SOAP) { | |
// encode soap card simulating soap control panel wiring for 533 (gasp!) | |
encode_soap_wiring(addr); | |
} else if (wiring == WIRING_IS) { | |
// encode floating point interpretive system (bell interpreter) card | |
encode_is_wiring(addr); | |
} else if (wiring == WIRING_IT) { | |
// encode Carnegie Internal Translator compiler card | |
encode_it_wiring(addr); | |
} else if (wiring == WIRING_8WORD) { | |
// encode 8 words per card | |
encode_8word_wiring(addr); | |
} else { | |
// default wiring: decode up to 8 numerical words per card | |
encode_8word_wiring(addr); | |
} | |
if ((card_nlpt == 1) && (card_lpt[0] == 0)) { | |
// skip this line printout & echo | |
} else { | |
/* echo? */ | |
encode_char(0, 13); encode_char(0, 10); | |
if (uptr->flags & UNIT_CARD_ECHO) { | |
for (i=0;i<card_nlpt;i++) sim_putchar(card_lpt[i]); | |
} | |
/* printout punched cards? */ | |
if (uptr->flags & UNIT_CARD_PRINT) { | |
// printout will be directed to file attached to CDP0 unit, if any | |
if (cdp_unit[0].flags & UNIT_ATT) { | |
sim_fwrite(card_lpt, 1, card_nlpt, cdp_unit[0].fileref); | |
} | |
} | |
} | |
// trim right spaces for printing punch card | |
card_buf[card_nbuf] = 0; | |
sim_debug(DEBUG_DETAIL, &cpu_dev, "Punch Card: %s\n", card_buf); | |
/* punch the cards */ | |
data = (struct _card_data *)uptr->up7; | |
for (i=0; i<80; i++) { | |
if (i >= card_nbuf) { | |
c = 32; | |
} else { | |
c = card_buf[i]; | |
} | |
if (c == 32) { | |
// no punch | |
data->image[i] = 0; | |
} else { | |
// punch char | |
h = ascii_to_hol[c & 127]; | |
data->image[i] = h; | |
} | |
} | |
sim_punch_card(uptr, NULL); | |
sim_debug(DEBUG_CMD, &cdp_dev, "PUNCH\n"); | |
uptr->u5 |= URCSTA_BUSY; | |
uptr->u6++; // incr number of punched cards | |
uptr->u5 &= ~URCSTA_BUSY; | |
return SCPE_OK; | |
} | |
/* Handle transfer of data for card punch */ | |
t_stat | |
cdp_srv(UNIT *uptr) { | |
// I/O is synchronous. No need to set up srv | |
return SCPE_OK; | |
} | |
/* Set card read/punch control panel wiring */ | |
t_stat cdp_set_wiring (UNIT *uptr, int32 val, CONST char *cptr, void *desc) | |
{ | |
int f; | |
if (uptr == NULL) return SCPE_IERR; | |
if (cptr == NULL) return SCPE_ARG; | |
for (f = 0; wirings[f].name != 0; f++) { | |
if (strcmp (cptr, wirings[f].name) == 0) { | |
uptr->flags = (uptr->flags & ~UNIT_CARD_WIRING) | wirings[f].mode; | |
return SCPE_OK; | |
} | |
} | |
return SCPE_ARG; | |
} | |
/* Show card read/punch control panel wiring */ | |
t_stat cdp_show_wiring (FILE *st, UNIT *uptr, int32 val, CONST void *desc) | |
{ | |
int f; | |
for (f = 0; wirings[f].name != 0; f++) { | |
if ((uptr->flags & UNIT_CARD_WIRING) == wirings[f].mode) { | |
fprintf (st, "%s wiring", wirings[f].name); | |
return SCPE_OK; | |
} | |
} | |
fprintf (st, "invalid control panel wiring (%d)", uptr->flags & UNIT_CARD_WIRING); | |
return SCPE_OK; | |
} | |
/* Set card read/punch echo to console */ | |
t_stat cdp_set_echo (UNIT *uptr, int32 val, CONST char *cptr, void *desc) | |
{ | |
int u = (uptr - cdp_unit); | |
t_stat r; | |
int num; | |
if (uptr == NULL) return SCPE_IERR; | |
if (cptr == NULL) { | |
num = 1; // no param means set (=1) | |
} else { | |
num = (int) get_uint (cptr, 10, 1, &r); | |
if (r != SCPE_OK) return r; | |
} | |
if (u == 0) { | |
sim_printf("this option cannot be set for CDP0\r\n"); | |
return SCPE_ARG; | |
} | |
switch(val) { | |
case 0: | |
if (num== 0) { | |
uptr->flags = uptr->flags & ~UNIT_CARD_ECHO; | |
} else { | |
uptr->flags = uptr->flags | UNIT_CARD_ECHO; | |
} | |
break; | |
case 1: | |
if (num== 0) { | |
uptr->flags = uptr->flags & ~UNIT_CARD_PRINT; | |
} else { | |
uptr->flags = uptr->flags | UNIT_CARD_PRINT; | |
} | |
break; | |
} | |
return SCPE_OK; | |
} | |
/* Show card read/punch control panel wiring */ | |
t_stat cdp_show_echo (FILE *st, UNIT *uptr, int32 val, CONST void *desc) | |
{ | |
switch(val) { | |
case 0: | |
fprintf (st, (uptr->flags & UNIT_CARD_ECHO) ? "ECHO": "No ECHO"); | |
break; | |
case 1: | |
fprintf (st, (uptr->flags & UNIT_CARD_PRINT) ? "PRINT": "No PRINT"); | |
break; | |
} | |
return SCPE_OK; | |
} | |
t_stat | |
cdp_attach(UNIT * uptr, CONST char *file) | |
{ | |
t_stat r; | |
r = sim_card_attach(uptr, file); | |
if (SCPE_BARE_STATUS(r) != SCPE_OK) | |
return r; | |
uptr->u5 = 0; | |
uptr->u6 = 0; // u6 = number of cards punched | |
return SCPE_OK; | |
} | |
t_stat | |
cdp_detach(UNIT * uptr) | |
{ | |
return sim_card_detach(uptr); | |
} | |
t_stat | |
cdp_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) | |
{ | |
fprintf (st, "%s\r\n\r\n", cdp_description(dptr)); | |
fprintf (st, "The 533 Card Read-punch writes cards using the selected\r\n"); | |
fprintf (st, "control panel wiring to set the format of punched cards.\r\n"); | |
fprintf (st, "It is possible to simulate a 407 accounting machine for\r\n"); | |
fprintf (st, "printing using SET CDP1 PRINT=1. In this case, punched\r\n"); | |
fprintf (st, "cards will be printed to file attached to unit 0 (CDP0).\r\n"); | |
fprintf (st, "SET CDP ECHO=1 will display on console cards printout.\r\n"); | |
sim_card_attach_help(st, dptr, uptr, flag, cptr); | |
fprint_set_help(st, dptr); | |
fprint_show_help(st, dptr); | |
return SCPE_OK; | |
} | |
const char * | |
cdp_description(DEVICE *dptr) | |
{ | |
return "533 Card Punch + 407 Accounting for printing"; | |
} | |