blob: 4b9649ef780721ce263916e1d13b5e8c2a5a8759 [file] [log] [blame] [raw]
/* i650_sys.c: IBM 650 Simulator system interface.
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.
*/
#include "i650_defs.h"
#include "sim_card.h"
#include <ctype.h>
/* SCP data structures and interface routines
sim_name simulator name string
sim_PC pointer to saved PC register descriptor
sim_emax number of words for examine
sim_devices array of pointers to simulated devices
sim_stop_messages array of pointers to stop messages
sim_load binary loader
*/
char sim_name[] = "IBM 650";
REG *sim_PC = &cpu_reg[0];
int32 sim_emax = 1;
DEVICE *sim_devices[] = {
&cpu_dev,
&cdr_dev,
&cdp_dev,
//XXX &mta_dev,
NULL
};
/* Device addressing words */
DIB cdr_dib = { 1, &cdr_cmd, NULL };
DIB cdp_dib = { 3, &cdp_cmd, NULL };
//XXX DIB mt_dib = { CH_TYP_76XX, NUM_UNITS_MT, 0000, 0000, &mt_cmd, &mt_ini };
/* Simulator stop codes */
const char *sim_stop_messages[] = {
"Unknown error",
"HALT instruction",
"Breakpoint",
"Unknown Opcode",
"Card Read/Punch Error",
"Programmed Stop",
"Overflow",
"Opcode Execution Error",
"Address Error",
0
};
static t_stat ibm650_deck_cmd(int32 arg, CONST char *buf);
static CTAB aux_cmds [] = {
/* Name Action Routine Argument Help String */
/* ---------- ----------------- --------- ----------- */
{ "CARDDECK", &ibm650_deck_cmd, 0, "Card Deck Operations: Join/Split/Print\n" },
{ NULL }
};
/* Simulator debug controls */
DEBTAB dev_debug[] = {
{"CMD", DEBUG_CMD},
{"DATA", DEBUG_DATA},
{"DETAIL", DEBUG_DETAIL},
{"EXP", DEBUG_EXP},
{0, 0}
};
DEBTAB crd_debug[] = {
{"CMD", DEBUG_CMD},
{"DATA", DEBUG_DATA},
{"DETAIL", DEBUG_DETAIL},
{"EXP", DEBUG_EXP},
{0, 0}
};
// simulator available IBM 533 wirings
struct card_wirings wirings[] = {
{WIRING_8WORD, "8WORD"},
{WIRING_SOAP, "SOAP"},
{WIRING_IS, "IS"},
{WIRING_IT, "IT"},
{WIRING_FORTRANSIT, "FORTRANSIT"},
{0, 0},
};
// code of char in IBM 650 memory
char mem_to_ascii[101] = {
/* 00 */ ' ', '~', '~', '~', '~', '~', '~', '~', '~', '~',
/* 10 */ '~', '~', '~', '~', '~', '~', '~', '~', '.', ')',
/* 20 */ '+', '~', '~', '~', '~', '~', '~', '~', '$', '*',
/* 30 */ '-', '/', '~', '~', '~', '~', '~', '~', ',', '(',
/* 40 */ '~', '~', '~', '~', '~', '~', '~', '~', '=', '-',
/* 50 */ '~', '~', '~', '~', '~', '~', '~', '~', '~', '~',
/* 60 */ '~', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
/* 70 */ '~', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
/* 80 */ '~', '~', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
/* 90 */ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
0};
// representation of word digit 0-9 in card including Y(12) and X(11) punchs
char digits_ascii[31] = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', /* 0-9 */
'?', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', /* 0-9 w/HiPunch Y(12) */
'!', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', /* 0-9 w/Negative Punch X(11) */
0};
uint16 ascii_to_hol[128] = {
/* Control */
0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000, /*0-37*/
/*Control*/
0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,
/*Control*/
0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,
/*Control*/
0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,0xf000,
/* sp ! " # $ % & ' */
/* none Y28 78 T28 Y38 T48 XT 48 */
0x000, 0x600, 0x006, 0x282, 0x442, 0x222, 0xA00, 0x022, /* 40 - 77 */
/* ( ) * + , - . / */
/* T48 X48 Y48 X T38 T X38 T1 */
0x222, 0x822, 0x422, 0x800, 0x242, 0x400, 0x842, 0x300,
/* 0 1 2 3 4 5 6 7 */
/* T 1 2 3 4 5 6 7 */
0x200, 0x100, 0x080, 0x040, 0x020, 0x010, 0x008, 0x004,
/* 8 9 : ; < = > ? */
/* 8 9 58 Y68 X68 38 68 X28 */
0x002, 0x001, 0x012, 0x40A, 0x80A, 0x042, 0x00A, 0x882,
/* @ A B C D E F G */
/* 82 X1 X2 X3 X4 X5 X6 X7 */
0x022, 0x900, 0x880, 0x840, 0x820, 0x810, 0x808, 0x804, /* 100 - 137 */
/* H I J K L M N O */
/* X8 X9 Y1 Y2 Y3 Y4 Y5 Y6 */
0x802, 0x801, 0x500, 0x480, 0x440, 0x420, 0x410, 0x408,
/* P Q R S T U V W */
/* Y7 Y8 Y9 T2 T3 T4 T5 T6 */
0x404, 0x402, 0x401, 0x280, 0x240, 0x220, 0x210, 0x208,
/* X Y Z [ \ ] ^ _ */
/* T7 T8 T9 X58 X68 T58 T78 28 */
0x204, 0x202, 0x201, 0x812, 0x20A, 0x412, 0x406, 0x082,
/* ` a b c d e f g */
0x212, 0xB00, 0xA80, 0xA40, 0xA20, 0xA10, 0xA08, 0xA04, /* 140 - 177 */
/* h i j k l m n o */
0xA02, 0xA01, 0xD00, 0xC80, 0xC40, 0xC20, 0xC10, 0xC08,
/* p q r s t u v w */
0xC04, 0xC02, 0xC01, 0x680, 0x640, 0x620, 0x610, 0x608,
/* x y z { | } ~ del */
/* Y78 X78 78 79 */
0x604, 0x602, 0x601, 0x406, 0x806,0x0006,0x0005,0xf000
};
/* Initialize vm */
void
vm_init(void) {
int i;
// Initialize vm memory to all plus zero
for(i = 0; i < MAXDRUMSIZE; i++) DRUM[i] = DRUM_NegativeZeroFlag[i] = 0;
for(i = 0; i < 60; i++) IAS[i] = IAS_NegativeZeroFlag[i] = 0;
// init specific commands
sim_vm_cmd = aux_cmds; /* set up the auxiliary command table */
}
void (*sim_vm_init) (void) = &vm_init;
/* Load a card image file into memory. */
t_stat
sim_load(FILE * fileref, CONST char *cptr, CONST char *fnam, int flag)
{
/* Currently not implimented until I know format of load files */
return SCPE_NOFNC;
}
/* Opcodes */
t_opcode base_ops[100] = {
// opcode name soap name R/W? option Valid Data Address
{OP_NOOP, "NOOP", "NOP", 0, 0, vda_DAITS},
{OP_STOP, "STOP", "HLT", 0, 0, vda_DAITS},
{OP_UFA, "FASN", "UFA", opReadDA, opStorUnit, vda_DAIS},
{OP_RTC, "RCT", "RTC", 0, opCntrlUnit, vda_T},
{OP_RTN, "RT", "RTN", 0, opCntrlUnit, vda_T},
{OP_RTA, "RTA", "RTA", 0, opCntrlUnit, vda_T},
{OP_WTN, "WT", "WTN", 0, opCntrlUnit, vda_T},
{OP_WTA, "WTA", "WTA", 0, opCntrlUnit, vda_T},
{OP_LIB, "LBB", "LIB", opReadDA, opStorUnit, vda_D, IL_IAS},
{OP_LDI, "LB", "LDI", opReadDA, opStorUnit, vda_D, IL_IAS},
{OP_AU, "AU", "AUP", opReadDA, 0, vda_DAIS},
{OP_SU, "SU", "SUP", opReadDA, 0, vda_DAIS},
{12, NULL, NULL, 0, 0, 0},
{13, NULL, NULL, 0, 0, 0},
{OP_DIV, "DIV", "DIV", opReadDA, 0, vda_DAIS},
{OP_AL, "AL", "ALO", opReadDA, 0, vda_DAIS},
{OP_SL, "SL", "SLO", opReadDA, 0, vda_DAIS},
{OP_AABL, "AABL", "AML", opReadDA, 0, vda_DAIS},
{OP_SABL, "SABL", "SML", opReadDA, 0, vda_DAIS},
{OP_MULT, "MULT", "MPY", opReadDA, 0, vda_DAIS},
{OP_STL, "STL", "STL", opWriteDA, 0, vda_DS},
{OP_STU, "STU", "STU", opWriteDA, 0, vda_DS},
{OP_STDA, "STDA", "SDA", opWriteDA, 0, vda_DS},
{OP_STIA, "STIA", "SIA", opWriteDA, 0, vda_DS},
{OP_STD, "STD", "STD", opWriteDA, 0, vda_DS},
{OP_NTS, "BNTS", "NTS", 0, opCntrlUnit, vda_DAIS},
{OP_BIN, "BIN", "BIN", 0, opCntrlUnit, vda_D},
{OP_SET, "SET", "SET", 0, opStorUnit, vda_S, IL_IAS},
{OP_SIB, "STBB", "SIB", 0, opStorUnit, vda_D, IL_IAS},
{OP_STI, "STB", "STI", 0, opStorUnit, vda_D, IL_IAS},
{OP_SRT, "SRT", "SRT", 0, 0, vda_DAITS},
{OP_SRD, "SRD", "SRD", 0, 0, vda_DAITS},
{OP_FAD, "FA", "FAD", opReadDA, opStorUnit, vda_DAIS},
{OP_FSB, "FS", "FSB", opReadDA, opStorUnit, vda_DAIS},
{OP_FDV, "FD", "FDV", opReadDA, opStorUnit, vda_DAIS},
{OP_SLT, "SLT", "SLT", 0, 0, vda_DAITS},
{OP_SCT, "SCT", "SCT", 0, 0, vda_DAITS},
{OP_FAM, "FAAB", "FAM", opReadDA, opStorUnit, vda_DAIS},
{OP_FSM, "FSAB", "FSM", opReadDA, opStorUnit, vda_DAIS},
{OP_FMP, "FM", "FMP", opReadDA, opStorUnit, vda_DAIS},
{OP_NZA, "BNZA", "NZA", 0, opStorUnit, vda_DAIS},
{OP_BMA, "BMNA", "BMA", 0, opStorUnit, vda_DAIS},
{OP_NZB, "BNZB", "NZB", 0, opStorUnit, vda_DAIS},
{OP_BMB, "BMNB", "BMB", 0, opStorUnit, vda_DAIS},
{OP_BRNZU, "BRNZU", "NZU", 0, 0, vda_DAIS},
{OP_BRNZ, "BRNZ", "NZE", 0, 0, vda_DAIS},
{OP_BRMIN, "BRMIN", "BMI", 0, 0, vda_DAIS},
{OP_BROV, "BROV", "BOV", 0, 0, vda_DAIS},
{OP_NZC, "BNZC", "NZC", 0, opStorUnit, vda_DAIS},
{OP_BMC, "BMNC", "BMC", 0, opStorUnit, vda_DAIS},
{OP_AXA, "AA", "AXA", 0, opStorUnit, vda_DAS},
{OP_SXA, "SA", "SXA", 0, opStorUnit, vda_DAS},
{OP_AXB, "AB", "AXB", 0, opStorUnit, vda_DAS},
{OP_SXB, "SB", "SXB", 0, opStorUnit, vda_DAS},
{OP_NEF, "BRNEF", "NEF", 0, opCntrlUnit, vda_DAIS},
{OP_RWD, "RWD", "RWD", 0, opCntrlUnit, vda_T},
{OP_WTM, "WTM", "WTM", 0, opCntrlUnit, vda_T},
{OP_BST, "BSP", "BST", 0, opCntrlUnit, vda_T},
{OP_AXC, "AC", "AXC", 0, opStorUnit, vda_DAS},
{OP_SXC, "SC", "SXC", 0, opStorUnit, vda_DAS},
{OP_RAU, "RAU", "RAU", opReadDA, 0, vda_DAIS},
{OP_RSU, "RSU", "RSU", opReadDA, 0, vda_DAIS},
{62, NULL, NULL, 0, 0, 0},
{63, NULL, NULL, 0, 0, 0},
{OP_DIVRU, "DIVRU", "DVR", opReadDA, 0, vda_DAIS},
{OP_RAL, "RAL", "RAL", opReadDA, 0, vda_DAIS},
{OP_RSL, "RSL", "RSL", opReadDA, 0, vda_DAIS},
{OP_RAABL, "RAABL", "RAM", opReadDA, 0, vda_DAIS},
{OP_RSABL, "RSABL", "RSM", opReadDA, 0, vda_DAIS},
{OP_LD, "LD", "LDD", opReadDA, 0, vda_DAIS},
{OP_RD, "RD", "RD1", 0, 0, vda_DS, IL_RD1},
{OP_PCH, "PCH", "WR1", 0, 0, vda_DS, IL_WR1},
{OP_RC1, "RC1", "RC1", 0, opStorUnit, vda_DS, IL_RD1},
{OP_RD2, "RD2", "RD2", 0, opStorUnit, vda_DS, IL_RD23},
{OP_WR2, "WR2", "WR2", 0, opStorUnit, vda_DS, IL_WR23},
{OP_RC2, "RC2", "RC2", 0, opStorUnit, vda_DS, IL_RD23},
{OP_RD3, "RD3", "RD3", 0, opStorUnit, vda_DS, IL_RD23},
{OP_WR3, "WR3", "WR3", 0, opStorUnit, vda_DS, IL_WR23},
{OP_RC3, "RC3", "RC3", 0, opStorUnit, vda_DS, IL_RD23},
{OP_RPY, "RPY", "RPY", 0, opCntrlUnit, vda_D},
{OP_RAA, "RAA", "RAA", 0, opStorUnit, vda_DAS},
{OP_RSA, "RSA", "RSA", 0, opStorUnit, vda_DAS},
{OP_RAB, "RAB", "RAB", 0, opStorUnit, vda_DAS},
{OP_RSB, "RSB", "RSB", 0, opStorUnit, vda_DAS},
{OP_TLU, "TLU", "TLU", 0, 0, vda_DS},
{OP_SDS, "SDS", "SDS", 0, opCntrlUnit, vda_9000},
{OP_RDS, "RDS", "RDS", 0, opCntrlUnit, vda_9000},
{OP_WDS, "WDS", "WDS", 0, opCntrlUnit, vda_9000},
{OP_RAC, "RAC", "RAC", 0, opStorUnit, vda_DAS},
{OP_RSC, "RSC", "RSC", 0, opStorUnit, vda_DAS},
{OP_BRD10, "BRD10", "BDO", 0, 0, vda_DAIS},
{OP_BRD1, "BRD1", "BD1", 0, 0, vda_DAIS},
{OP_BRD2, "BRD2", "BD2", 0, 0, vda_DAIS},
{OP_BRD3, "BRD3", "BD3", 0, 0, vda_DAIS},
{OP_BRD4, "BRD4", "BD4", 0, 0, vda_DAIS},
{OP_BRD5, "BRD5", "BD5", 0, 0, vda_DAIS},
{OP_BRD6, "BRD6", "BD6", 0, 0, vda_DAIS},
{OP_BRD7, "BRD7", "BD7", 0, 0, vda_DAIS},
{OP_BRD8, "BRD8", "BD8", 0, 0, vda_DAIS},
{OP_BRD9, "BRD9", "BD9", 0, 0, vda_DAIS}
};
/* Print out an instruction */
void
print_opcode(FILE * of, t_int64 val)
{
int sgn;
int IA;
int DA;
int op;
int n;
CONST char * opname;
if (val < 0) {sgn = -1; val = -val;} else sgn = 1;
opname = DecodeOpcode(val, &op, &DA, &IA);
if (opname == NULL) {
fprintf(of, " %d Unknown opcode", op);
return;
}
fputs(opname, of);
n = strlen(opname);
while (n++<6) fputc(' ', of);
fprintf(of, "%04d ", DA);
fputc(' ', of);
fprintf(of, "%04d ", IA);
}
/* Symbolic decode
Inputs:
*of = output stream
addr = current PC
*val = pointer to values
*uptr = pointer to unit
sw = switches
Outputs:
return = status code
*/
t_stat
fprint_sym(FILE * of, t_addr addr, t_value * val, UNIT * uptr, int32 sw)
{
t_int64 d, inst;
int NegZero;
int ch;
if (*val == NEGZERO_value) {
inst = 0;
NegZero = 1;
} else {
inst = *val;
NegZero = 0;
}
/* Print value in decimal */
fputc(' ', of);
fprintf(of, "%06d%04d%c", printfw(inst,NegZero)); // fprintf 10 digits word n, with sign
inst = AbsWord(inst);
if (sw & SWMASK('C') ) {
int i;
d = inst;
fputs(" '", of);
for (i=0;i<5;i++) {
ch = Shift_Digits(&d, 2);
fputc(mem_to_ascii[ch], of);
}
fputc('\'', of);
}
if (sw & SWMASK('M')) {
fputs(" ", of);
inst = AbsWord(inst);
print_opcode(of, inst);
}
return SCPE_OK;
}
int
find_opcode(char *op)
{
int i;
if (op == NULL) return -1;
for (i=0;i<100;i++) {
if (base_ops[i].name1 == NULL) continue;
// accept both mnemonic sets: operation manual one (name1) and soap one (name2)
if ((base_ops[i].name1 != NULL) && (strcmp(op, base_ops[i].name1) == 0))
return i;
if ((base_ops[i].name2 != NULL) && (strcmp(op, base_ops[i].name2) == 0))
return i;
}
return -1;
}
/* read n digits, optionally with sign NNNN[+|-]
Inputs:
*cptr = pointer to input string
sgnFlag = 1 to allow signed value
Outputs:
d = parsed value
*/
CONST char * parse_sgn(int *neg, CONST char *cptr)
{
*neg=0;
while (isspace(*cptr)) cptr++;
if (*cptr == '+') {
cptr++;
} else if (*cptr == '-') {
cptr++; *neg = 1;
}
return cptr;
}
CONST char * parse_n(t_int64 *d, CONST char *cptr, int n)
{
int i = 0;
*d = 0;
while (1) {
if ((n == 10) && (isspace(*cptr))) {
cptr++; // on 10 digit words, allow spaces
continue;
}
if (*cptr < '0' || *cptr > '9') break;
if (i++ > n) {
cptr++;
} else {
*d = (*d * 10) + (*cptr++ - '0');
}
}
if (n == 4) {*d = *d % D4; } else
if (n == 10) {*d = *d % D10;}
return cptr;
}
/* Symbolic input
Inputs:
*cptr = pointer to input string
addr = current PC
uptr = pointer to unit
*val = pointer to output values
sw = switches
Outputs:
status = error status
*/
// convert ascii char to two digits IBM 650 code
int ascii_to_NN(int ch)
{
int i;
if ((ch >= 'a') && (ch <= 'z')) ch = ch -'a'+'A';
for (i=0;i<100;i++) if (mem_to_ascii[i] == ch) return i;
return 0;
}
t_stat parse_sym(CONST char *cptr, t_addr addr, UNIT * uptr, t_value * val, int32 sw)
{
t_int64 d;
int op, da, ia;
char ch, opcode[100];
int i;
int neg, IsNeg;
while (isspace(*cptr)) cptr++;
d = 0; IsNeg = 0;
if (sw & SWMASK('M')) {
/* Grab opcode */
cptr = parse_sgn(&neg, cptr);
if (neg) IsNeg = 1;
cptr = get_glyph(cptr, opcode, 0);
op = find_opcode(opcode);
if (op < 0) return STOP_UUO;
if (DecodeOpcode(op * (t_int64) D8, &op, &da, &ia) == NULL) {
// opcode exists, but not availble because associated hw (Storage Unit or Control Unit)
// is not enabled
return STOP_UUO;
}
while (isspace(*cptr)) cptr++;
/* Collect first argument: da */
cptr = parse_n(&d, cptr, 4);
da = (int) d;
/* Skip blanks */
while (isspace(*cptr)) cptr++;
/* Collect second argument: ia */
cptr = parse_n(&d, cptr, 4);
ia = (int) d;
// construct inst
d = op * (t_int64) D8 + da * (t_int64) D4 + (t_int64) ia;
} else if (sw & SWMASK('C')) {
d = 0;
if ((*cptr == 34) || (*cptr == 39)) cptr++; // skip double or single quotes if present
for(i=0; i<5;i++) {
d = d * 100;
ch = *cptr;
if (ch == '\0') continue;
if ((*cptr == 34) || (*cptr == 39)) continue; // double or single quotes mark end of text
cptr++;
d = d + ascii_to_NN(ch);
}
} else {
cptr = parse_sgn(&neg, cptr);
if (neg) IsNeg = 1;
cptr = parse_n(&d, cptr, 10);
}
cptr = parse_sgn(&neg, cptr);
if (neg) IsNeg = 1;
if ((IsNeg) && (d == 0)) {
*val = NEGZERO_value; // val has this special value to represent -0 (minus zero == negative zero)
} else {
if (IsNeg) d=-d;
*val = (t_value) d;
}
return SCPE_OK;
}
/* Helper functions */
// set in buf string ascii chars form word d ( chars: c1c2c3c4c5 )
// starts at char start (1..5), for CharLen chars (0..5)
// to convert the full word use (buf, 1, 5, d)
char * word_to_ascii(char * buf, int CharStart, int CharLen, t_int64 d)
{
int i,c1,c2;
char * buf0;
buf0 = buf; // save start of buffer
for (i=0;i<5;i++) { // 5 alpha chars per word
c1 = Shift_Digits(&d, 2);
c2 = mem_to_ascii[c1];
if (i < CharStart-1) continue;
if (i >= CharStart+CharLen-1) continue;
*buf++ = c2;
}
*buf++ = 0;
return buf0;
}
// return hi digit (digit 10) al leftmost position in number (no sign)
int Get_HiDigit(t_int64 d)
{
return (int) ((AbsWord(d) * 10) / D10);
}
// shift d value for nDigits positions (max 7)
// if nDigit > 0 shift left, if < 0 then shift right
// return value of shifted digits (without sign)
int Shift_Digits(t_int64 * d, int nDigits)
{
int i,n;
int neg = 0;
if (nDigits == 0) return 0; // no shift
if (*d < 0) {*d=-*d; neg = 1;}
n = 0;
if (nDigits > 0) { // shift left
for (i=0;i<nDigits;i++) {
n = n * 10 + (int) (*d / (1000000000L)); // nine digits (9 zeroes)
*d = (*d % (1000000000L)) * 10;
}
} else { // shift right
for (i=0;i<-nDigits;i++) {
n = *d % 10;
*d = *d / 10;
}
}
if (neg) *d=-*d;
return n;
}
/* deck operations
carddeck [-q] <operation> <parameters...>
allowed operations are split, join, print
default format for card files is AUTO, this allow to intermix source decks
with different formats. To set the format for carddeck operations use
set cpr0 -format xxxx
this will apply to all operations, both on reading and writing deck files
carddeck split split the deck being punched in IBM 533 device in two separate destination decks
carddeck split <count> <dev|file0> <file1> <file2>
<dev> should be cdp1 to cdp3. File must be attached. The cards punched on
this file are the ones on source deck to split.
<file0> if instead of cdp1, cdp2 or cdp3, a file can be specified containing
the source deck to be splitted
<count> number of cards in each splitted deck.
If count > 0, indicates the cards on first destination deck file
remaining cards go to the second destination deck
If count < 0, indicates the cards on second destination deck file
(so deck 2 contains lasts count cards from source)
<file1> first destination deck file
<file2> second destination deck file
when using <dev> as source both <file1> or <file2> can have same name as the currently
attached file to cdp device. On command execution, cdp gest its file detached.
file1 and file are created (overwritten if already exists).
when using <file0> as source both <file1> or <file2> can have same name as <file0>.
<file0> is completly read by SimH in its internal buffer (room for 10K cards)
and then splitted to <file1> and <file2>.
carddeck join join several deck files into a new one
carddeck join <file1> <file2> ... as <file>
<file1> first source deck file
<file2> second source deck file
...
<file> destination deck file
any source file <file1>, <file2>, etc can have same name as destination file <file>.
Each source file is completly read in turn by SimH in its internal buffer (room for 10K cards)
and then written on destination file. This allos to append une deck on the top/end of
another one.
carddeck print print deck on console, and on simulated IBM 407 is any file is attached to cpd0
carddeck print <file>
carddeck echolast echo on console last n cards already read that are in the take hopper
carddeck echolasty <count> <dev>
<count> number of cards to display (upo to 10)
<dev> should be cdr1 to cdr3. Unit for Take hopper
switches: if present mut be just after carddeck and before deck operation
-Q quiet return status.
*/
// load card file fn and add its cards to
// DeckImage array, up to a max of nMaxCards
// increment nCards with the number of added cards
// uses cdr0 device/unit
t_stat deck_load(CONST char *fn, uint16 * DeckImage, int * nCards)
{
UNIT * uptr = &cdr_unit[0];
struct _card_data *data;
t_stat r, r2;
int i, convert_to_ascii;
uint16 c;
if (*nCards < 0) {
*nCards = 0;
convert_to_ascii = 1;
} else {
convert_to_ascii = 0;
}
// set flags for read only
uptr->flags |= UNIT_RO;
// attach file to cdr unit 0
r = (cdr_dev.attach)(uptr, fn);
if (r != SCPE_OK) return r;
// read all cards from file
while (1) {
if (*nCards >= MAX_CARDS_IN_DECK) {
r = sim_messagef (SCPE_IERR, "Too many cards\n");
break;
}
r = sim_read_card(uptr);
if (r == SCPE_EOF) {
r = SCPE_OK; break; // normal termination on card file read finished
} else if (r != SCPE_OK) break; // abnormal termination on error
data = (struct _card_data *)uptr->up7;
// add card read to deck
for (i=0; i<80; i++) {
c = data->image[i];
if (convert_to_ascii) c = data->hol_to_ascii[c];
DeckImage[*nCards * 80 + i] = c;
}
*nCards = *nCards + 1;
}
// deattach file from cdr unit 0
r2 = (cdr_dev.detach)(uptr);
if (r == SCPE_OK) r = r2;
if (r != SCPE_OK) return r;
return SCPE_OK;
}
// write nCards starting at card from DeckImage array to file fn
// uses cdr0 device/unit
t_stat deck_save(CONST char *fn, uint16 * DeckImage, int card, int nCards)
{
UNIT * uptr = &cdr_unit[0];
struct _card_data *data;
t_stat r;
int i,nc;
// set flags for create new file
uptr->flags &= ~UNIT_RO;
sim_switches |= SWMASK ('N');
// attach file to cdr unit 0
r = (cdr_dev.attach)(uptr, fn);
if (r != SCPE_OK) return r;
// write cards to file
for (nc=0;nc<nCards;nc++) {
if (nc + card >= MAX_CARDS_IN_DECK) {
r = sim_messagef (SCPE_IERR, "Reading outside of Deck\n");
break;
}
data = (struct _card_data *)uptr->up7;
// read card from deck
for (i=0; i<80; i++) data->image[i] = DeckImage[(nc + card) * 80 + i];
r = sim_punch_card(uptr, NULL);
if (r != SCPE_OK) break; // abnormal termination on error
}
// deattach file from cdr unit 0
(cdr_dev.detach)(uptr);
return r;
}
// echo/print nCards from DeckImage array
// uses cdp0 device/unit
void deck_print_echo(uint16 * DeckImage, int nCards, int bPrint, int bEcho)
{
char line[81];
int i,c,nc;
for (nc=0; nc<nCards; nc++) {
// read card, check and, store in line
for (i=0;i<80;i++) {
c = DeckImage[nc * 80 + i];
c = toupper(c); // IBM 407 can only print uppercase
if ((c == '?') || (c == '!')) c = '0'; // remove Y(12) or X(11) punch on zero
if (strchr(mem_to_ascii, c) == 0) c = ' '; // space if not in IBM 650 character set
line[i] = c;
}
line[80]=0;
sim_trim_endspc(line);
// echo on console (add CR LF)
if (bEcho) {
for (i=0;i<(int)strlen(line);i++) sim_putchar(line[i]);
sim_putchar(13);sim_putchar(10);
}
// printout will be directed to file attached to CDP0 unit, if any
if ((bPrint) && (cdp_unit[0].flags & UNIT_ATT)) {
sim_fwrite(line, 1, strlen(line), cdp_unit[0].fileref); // fwrite clears line!
line[0] = 13; line[1] = 10; line[2] = 0;
sim_fwrite(line, 1, 2, cdp_unit[0].fileref);
}
}
}
// carddeck split <count> <dev|file0> <file1> <file2>
static t_stat deck_split_cmd(CONST char *cptr)
{
char fn0[4*CBUFSIZE];
char fn1[4*CBUFSIZE];
char fn2[4*CBUFSIZE];
char gbuf[4*CBUFSIZE];
DEVICE *dptr;
UNIT *uptr;
t_stat r;
uint16 DeckImage[80 * MAX_CARDS_IN_DECK];
int nCards, nCards1, tail;
while (sim_isspace (*cptr)) cptr++; // trim leading spc
if (*cptr == '-') {
tail = 1;
cptr++;
} else {
tail = 0;
}
cptr = get_glyph (cptr, gbuf, 0); // get cards count param
nCards1 = (int32) get_uint (gbuf, 10, 10000, &r);
if (r != SCPE_OK) return sim_messagef (SCPE_ARG, "Invalid count value\n");
if (nCards1 == 0) return sim_messagef (SCPE_ARG, "Count cannot be zero\n");
get_glyph (cptr, gbuf, 0); // get dev param
cptr = get_glyph_quoted (cptr, fn0, 0); // re-read using get_glyph_quoted to do not
// change the capitalization of file name
if ((strlen(gbuf) != 4) || (strncmp(gbuf, "CDP", 3)) ||
(gbuf[3] < '1') || (gbuf[3] > '3') ) {
// is a file
} else {
// is cdp1 cdp2 or cdp3 device
dptr = find_unit (gbuf, &uptr); /* locate unit */
if (dptr == NULL) /* found dev? */
return SCPE_NXDEV;
if (uptr == NULL) /* valid unit? */
return SCPE_NXUN;
if ((uptr->flags & UNIT_ATT) == 0) /* attached? */
return SCPE_NOTATT;
// get the file name
strcpy(fn0, uptr->filename);
sim_card_detach(uptr); // detach file from cdp device to be splitted
}
// read source deck
nCards = 0;
r = deck_load(fn0, DeckImage, &nCards);
if (r != SCPE_OK) return sim_messagef (r, "Cannot read source deck (%s)\n", fn0);
// calc nCards1 = cards in first deck
if (tail) {
// calc cards remaining when last nCardCount are removed from source deck
nCards1 = nCards - nCards1;
if (nCards1 < 0) nCards1 = 0;
}
if (nCards1 > nCards) nCards1 = nCards;
while (sim_isspace (*cptr)) cptr++; // trim leading spc
cptr = get_glyph_quoted (cptr, fn1, 0); // get next param: filename 1
if (fn1[0] == 0) return sim_messagef (SCPE_ARG, "Missing first filename\n");
while (sim_isspace (*cptr)) cptr++; // trim leading spc
cptr = get_glyph_quoted (cptr, fn2, 0); // get next param: filename 2
if (fn2[0] == 0) return sim_messagef (SCPE_ARG, "Missing second filename\n");
r = deck_save(fn1, DeckImage, 0, nCards1);
if (r != SCPE_OK) return sim_messagef (r, "Cannot write destination deck1 (%s)\n", fn0);
r = deck_save(fn2, DeckImage, nCards1, nCards-nCards1);
if (r != SCPE_OK) return sim_messagef (r, "Cannot write destination deck2 (%s)\n", fn0);
if ((sim_switches & SWMASK ('Q')) == 0) {
sim_messagef (SCPE_OK, "Deck splitted to %d/%d cards\n", nCards1, nCards-nCards1);
}
return SCPE_OK;
}
// carddeck join <file1> <file2> ... as <file>
static t_stat deck_join_cmd(CONST char *cptr)
{
char fnSrc[4*CBUFSIZE];
char fnDest[4*CBUFSIZE];
CONST char *cptr0;
CONST char *cptrAS;
char gbuf[4*CBUFSIZE];
t_stat r;
uint16 DeckImage[80 * MAX_CARDS_IN_DECK];
int i,nDeck, nCards, nCards1;
cptr0 = cptr;
// look for "as"
while (*cptr) {
while (sim_isspace (*cptr)) cptr++; // trim leading spc
cptrAS = cptr; // mark position of AS
cptr = get_glyph_quoted (cptr, gbuf, 0); // get next param
if (gbuf[0] == 0) return sim_messagef (SCPE_ARG, "AS <file> not found\n");
for (i=0;i<2;i++) gbuf[i] = sim_toupper(gbuf[i]);
if (strcmp(gbuf, "AS") == 0) break;
}
while (sim_isspace (*cptr)) cptr++; // trim leading spc
cptr = get_glyph_quoted (cptr, fnDest, 0); // get next param: destination filename
if (fnDest[0] == 0) return sim_messagef (SCPE_ARG, "Missing destination filename\n");
if (*cptr) return sim_messagef (SCPE_ARG, "Extra unknown parameters after destination filename\n");
cptr = cptr0; // restore cptr to scan source filenames
nDeck = nCards = 0;
memset(DeckImage, 0, sizeof(DeckImage));
while (1) {
while (sim_isspace (*cptr)) cptr++; // trim leading spc
if (cptrAS == cptr) break; // break if reach "AS"
cptr = get_glyph_quoted (cptr, fnSrc, 0); // get next param: source filename
if (fnSrc[0] == 0) return sim_messagef (SCPE_ARG, "Missing source filename\n");
// read source deck
nCards1 = nCards;
r = deck_load(fnSrc, DeckImage, &nCards);
if (r != SCPE_OK) return sim_messagef (r, "Cannot read source deck (%s)\n", fnSrc);
nDeck++;
if ((sim_switches & SWMASK ('Q')) == 0) {
sim_messagef (SCPE_OK, "Source Deck %d has %d cards (%s)\n", nDeck, nCards - nCards1, fnSrc);
}
}
r = deck_save(fnDest, DeckImage, 0, nCards);
if (r != SCPE_OK) return sim_messagef (r, "Cannot write destination deck (%s)\n", fnDest);
if ((sim_switches & SWMASK ('Q')) == 0) {
sim_messagef (SCPE_OK, "Destination Deck has %d cards (%s)\n", nCards, fnDest);
}
return SCPE_OK;
}
// carddeck print <file>
static t_stat deck_print_cmd(CONST char *cptr)
{
char fn[4*CBUFSIZE];
t_stat r;
uint16 DeckImage[80 * MAX_CARDS_IN_DECK];
int nCards;
while (sim_isspace (*cptr)) cptr++; // trim leading spc
cptr = get_glyph_quoted (cptr, fn, 0); // get next param: source filename
if (fn[0] == 0) return sim_messagef (SCPE_ARG, "Missing filename\n");
if (*cptr) return sim_messagef (SCPE_ARG, "Extra unknown parameters after filename\n");
// read deck to be printed (-1 to convert to ascii value, not hol)
nCards = -1;
r = deck_load(fn, DeckImage, &nCards);
if (r != SCPE_OK) return sim_messagef (r, "Cannot read deck to print (%s)\n", fn);
deck_print_echo(DeckImage, nCards, 1,1);
if ((sim_switches & SWMASK ('Q')) == 0) {
sim_messagef (SCPE_OK, "Printed Deck with %d cards (%s)\n", nCards, fn);
}
return SCPE_OK;
}
// carddeck echolast <dev> <count>
static t_stat deck_echolast_cmd(CONST char *cptr)
{
char gbuf[4*CBUFSIZE];
t_stat r;
uint16 DeckImage[80 * MAX_CARDS_IN_DECK];
int i,nc,nCards, ic, nh, ncdr;
while (sim_isspace (*cptr)) cptr++; // trim leading spc
cptr = get_glyph (cptr, gbuf, 0); // get cards count param
nCards = (int32) get_uint (gbuf, 10, MAX_CARDS_IN_READ_TAKE_HOPPER, &r);
if (r != SCPE_OK) return sim_messagef (SCPE_ARG, "Invalid count value\n");
if (nCards == 0) return sim_messagef (SCPE_ARG, "Count cannot be zero\n");
cptr = get_glyph (cptr, gbuf, 0); // get dev param
if ((strlen(gbuf) != 4) || (strncmp(gbuf, "CDR", 3)) ||
(gbuf[3] < '1') || (gbuf[3] > '3') ) {
return sim_messagef (SCPE_ARG, "Device should be CDR1 CDR2 or CDR3\n");
}
ncdr = gbuf[3] - '1'; // ncdr=0 for cdr1, =1 for cdr2, and so on
if ((ncdr >= 0) && (ncdr < 3)){
// safety check
} else {
return sim_messagef (SCPE_ARG, "Invalid Device number\n");
}
if (*cptr) return sim_messagef (SCPE_ARG, "Extra unknown parameters\n");
// get nCards form read card take hopper buffer
// that is, print last nCards read
// get last nCards cards, so
// first card to echo is count ones before last one
nh = ReadHopperLast[ncdr] - (nCards-1);
nh = nh % MAX_CARDS_IN_READ_TAKE_HOPPER;
for (nc=0; nc<nCards; nc++) {
// copy card form read hopper buf to deck image
ic = (ncdr * MAX_CARDS_IN_READ_TAKE_HOPPER + nh) * 80;
for (i=0;i<80;i++) DeckImage[nc * 80 + i] = ReadHopper[ic + i];
// get previous read card
nh = (nh + 1) % MAX_CARDS_IN_READ_TAKE_HOPPER;
}
deck_print_echo(DeckImage, nCards, 0,1);
if ((sim_switches & SWMASK ('Q')) == 0) {
sim_messagef (SCPE_OK, "Last %d cards from Read take Hopper\n", nCards);
}
return SCPE_OK;
}
static t_stat ibm650_deck_cmd(int32 arg, CONST char *buf)
{
char gbuf[4*CBUFSIZE];
const char *cptr;
cptr = get_glyph (buf, gbuf, 0); // get next param
if (strcmp(gbuf, "-Q") == 0) {
sim_switches |= SWMASK ('Q');
cptr = get_glyph (cptr, gbuf, 0);
}
if (strcmp(gbuf, "JOIN") == 0) {
return deck_join_cmd(cptr);
}
if (strcmp(gbuf, "SPLIT") == 0) {
return deck_split_cmd(cptr);
}
if (strcmp(gbuf, "PRINT") == 0) {
return deck_print_cmd(cptr);
}
if (strcmp(gbuf, "ECHOLAST") == 0) {
return deck_echolast_cmd(cptr);
}
return sim_messagef (SCPE_ARG, "Unknown deck command operation\n");
}