// emulator for Leo III machine code (called "computer code" in Leo documentation) // Author: David Holdsworth -- David.Holdsworth@bcs.org // Currently all rights are reserved, but this will be made available under the GNU public license. // The current version of this program is available on: // http://sw.ccs.bcs.org/leo/leo3.c // N.B. This version will compile under Microsoft's Studio system -- see ifdef WIN32 bits // The binary program is in the form, so that it can be typed by hand // a/d/m n where a, d, m and n are decimal numbers // Anything to the right of n is ignored, as is any line starting with space or shorter than 5 chars // A companion Intercode translator generates binary programs in this form // A companion loader (getbin) extracts a binary program from a "mag tape" output by the original // Intercode Translator and outputs a file suitable for input to this program // A file in the current directory called routes.txt defines the configuration of peripherals. // Interrupts operate as per sect 17.1 of Vol I when running in master mode // First parameter is the file containing the binary program (see above for format). // Second parameter is the file of paper tape data (default ptr.txt). // There is a comment convention and an include facility for paper tape data -- see below // Verbosity is controlled by -v switch // 0 -- pretty well nothing except file open and close // 1 -- I/O traffic // 2 -- subroutine exits, master routine calls // 3 -- EDIT, UNPACK, long subtracts, subroutine entry // 4 -- all jumps // 6 -- every instruction // 7 -- all memory references // 8 -- data read from mag tape by action 19 // 9 -- modification chains and various bits internal to some instructions (see -t below) // Each level also includes all lower levels, i.e. -v3 includes all the output from 2, 1 and 0 // Switches are: // -v4 set verbosity to 4 // -t8888 turn on -v9 diagnostics after 8888 instructions // -a9999 abandon execution after 9999 instructions // -d write the diagnostics to file log.txt rather than standard output (useful with gdb) // -m7777 output diagnostic every time the value of location 7777 changes // -n6 output diagnostic every time the value of any location from 7777 to 7777+6-1 changes // limit is 20 locations // -w1450 suppress diagnostics on entry to subroutine at 1450; resume on exit // used for skipping wait loops that just burn CPU time // -A15 put the value 15 (i.e. 0xF) in A // -B9000 put the value 9000 in B // -BA9000 put the Leo III characters as octets "9000" = 00493 03030 in B // -BD9000 put the Leo III digits as quartets 00000 09000 in B // -BD9pet put the Leo III digits as quartets 00000 09FBA in B // -BD900E put the Leo III digits as quartets 00000 0900E in B // -b600 allow mag tape blocks up to 600 short words // -CD66E64 put the sterling radix in C -- decimal is the default for programmes with an extra chapter // -I600 put value 600 in the indicator register // -i1 set autonous peripheral interrupts just on typewriter // -i2 set autonous peripheral interrupts on everything // -c turn the clock interrupts on // -f7890 when execution first reaches address 7890 set -v9 and abandon after another 1000 instructions // -e echo command line input, useful when using > to create a console log file // -eh as above, but log file is assumed to be HTML and input is coloured red // -V use VT100/ANSI escape codes in typewriter output (i.e. stdout) // -o ops console on localhost port 8888 -- talks to opscons.jar // -p line printer on localhost port 8889 -- talks to printer.jar // -ocons ops console on host cons (can be any domain) port 8888 // -plp00 line printer on host lp00 (can be any domain) port 8889 // Leo III indicator switches -- unless you are using the -o switch // when the master routine reads these a query is put out (see indflag) // It can be answered using any of the values as per the -A or -B switches above // Also there are commands that can be used at this stage: // m02 MT-A3.leo will mount a file called MT-A3.leo on route 2 // pMRdata.txt will allocate the paper tape reader to MRdata.txt // x999 will abandon execution after another 999 instructions // x will abandon execution now // v7 will set the verbosity to 7 // vp will set the indicator prompt to versbose form giving instructions counters // d64 will print to log.txt 10 locations starting at 64 // d will print to log.txt 10 locations starting at the same address as previously // dd will print to log.txt 10 locations starting at the same address as previously + 10 // date will set the indicator register to today's date, for use on loading Master // f1 will print in format 1 (i.e. short words 10 to a line) // f2 will print in format 2 (i.e. one short word to a line with interpretation) // c look here will print the comment look here in log.txt // 1/3/11 will set the indicator register to 13B hex // a blank line will leave the indicator register unchanged // z789 will treat the next 789 readings of the indicator register as blank // Leo III indicator switches -- if you are using the -o switch // the indicator switches are emulated by a java program -- see LeoIIIdemo3 // Execution mode is controlled by done19 which is set when an action 19 is obeyed // This indicates that we must be in master mode, and can expect the interrupt servicing is set up, etc (?) // Comment convention for paper tape data // read one line of PTR data and strip comments // A line starting with [ as the very first character is ignored // A [ anywhere else causes everything to the right of it so be seen as comment // and spaces on its left to be removed // Thus if a line needs to end with a space, it cannot have a comment // Include facility for paper tape data // If the paper tape data file provided as the last parameter contains a line #include qwert.txt // the file qwert.txt will be read at this point, and at its end input with continue after the #include line // There are basically 3 modes of execution. // master mode -- when running a master routine // Intercode mode -- when running a program produced by the Leo Translator and getbin.c // transition mode -- when running the version of the Translator produced by it.c // The differences between the modes relates to the processing of I/O and system calls. // Intercode mode // A binary program produced by getbin.c contains the extra chapter that is a feature of // the interface between the binary program and the master routine. // When the loadprog routine encounters this extra chapter, I/O routes are set up and the // group 4 system calls lead to the routine group4() which then emulates the I/O function of a master // Master mode // The 19 actions of computer code are only legal when running in master mode (tag=14). // When such an action is obeyed it is assumed that we are running the master, and a flag is set // which removes the inhibit on interrupts. // The master routine generator 08004 actually makes the transition from Intercode mode to master mode. // I/O is handled by the implementation of action 19. Mag tape I/O is handled in mtio(). // In master mode instructions to read indicators sometimes produce a prompt to simulate the stack indicators key. // There are situation in which it can completely forget to issue invitation to type. // You can do a control-C to force the next read indicators to issue an invitation to type. // Transition mode // This mode was used historically for directly (sort of) implementing the group 4 Intercode actions, // before we knew what computer code was generated for these Intercode actons. // Now that we have the real Translator, this mode is obsolescent, and only used to demonstrate the bootstrap. // I/O is handled by the routine master() #include #include #include #include #include #include #include #include #include #ifndef WIN32 #include // includes for -o option #include #include #include #include #else #pragma warning(disable : 4996) #pragma warning(disable : 4703) #pragma comment(lib, "WS2_32.lib") #include #endif // include // see if it is a Microsoft compiler #ifdef WIN32 #include int ftruncate(int dv, __int64 sz) // deal with non-standard Windows I/O { return _chsize_s(dv, sz); } #else #include #endif #ifndef O_BINARY #define O_BINARY 0 #endif // stop grumbles from Visual studio #define _CRT_SECURE_NO_WARNINGS #define LINEEND 0x5E #define BLOCKEND 0x7E #define NUMEND 0x7D #define TWTAB 7 // typewriter tab spacing int verbosity = 0; // diagnostic parameters - very quiet by default int turnOn = -1; // turn on max diagnostics at this instruction count int turnOff = -1; // suppress diagnostics during this subroutine (e.g. PROCR 700 in 08004) int monloc = -1; // monitor change of value at this address int monval[20]; // can monitor up to 20 locations starting at monloc int monnum = 1; // number of locations to monitor int abandon = -1; // abandon execution after this number of instructions int faband = -1; // abandon execution 1000 instructions after first getting here int buffswap = 1; // set if master routine is to implement proper buffer swapping int count = 0; // number of instructions obeyed int intreq = -1; // an interrupt is requested when count reaches this value int lastint = 0; // diagnostic keeps address of last interrupt, seems master zeroises word 8 int maxblox = 54; // only allow mag tape blocks up to 54 by default int ioauto = 0; // autonomous peripherals are turned on by -i switch (for interrupt) int mstime = 0; // set to 1 to enable millisecond timer int nowleo; // time of start of run -- needed when clock word is overwritten int indflag = 0; // control interaction on INTERROGATE INDICATORS ... // ... NZ suppresses interaction, +ve value is decremented on each II int done19 = 0; // indicates that a 19 action has been obeyed -- i.e. we are in master mode int store[8192*4 + 2000]; // each short compartment is one word, right justified - 4 divisions // An extra 2000 words are used as a buffer for mag tape operations char tag[8192*4]; // tags are not checked at present -- so this may change except MR start kludge int sc; // sequence control register, i.e. program counter int instr; // currently obeyed instruction int msa, lsa, msb, lsb; // most significant half of A, etc int creg = 0; // five radix digits int topcreg = 0; // copies of the top digit in creg int tagreg = 0; // or should we start with 14 ?? int groupref = 8; // points to modifier group of current group (starts with group 1) int indreg = 0; // does this start as zero apart from group indicator int datereg = 0; // date as set in the indicator register on start up int msd, lsd, mse, lse, msf, lsf; // internal registers, not seen by programmer - may not use all of them int w4, w3, w2, w1; // 4 short cmps to receive results int nnn; // nnn is N as in the manual (now declared global) int startload = 160; // address of first word to be loaded int startexec = 160; // address of first instruction to be obeyed int *overlay[] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }; int oladdr[15], olsize[15]; int lastoverlay = 0; // address of start of last overlay -- subtracted from SC in many diagnostics FILE *diag; int quiet = 1; // do not list source by default int zzz = 0; // for diagnostic on TEST ROUTE int zzz2 = 0; // for diagnostic on TEST ROUTE char *prognum = "12345"; // programme number picked up in loadprog if poss char *ccstr[] = { "HALT", "Invalid", "REPLACE (B) BY (N)", "SET RADIX", "COPY REGISTERS", "REPLACE REGISTERS", "REPLACE (B) BY (N)", "Invalid", "TABLE LOOK UP", "PREPARE FOR DIGIT COLLATION", "ROUND OFF", "INTERCHANGE AREA ADDRESSES", "TABLE LOOK UP", "ADD LITERAL ADDRESS", "SUBTRACT LITERAL ADDRESS", "SELECT LITERAL ADDRESS ", "TRANSFER (A) TO N", "TRANSFER (A) TO N", "TRANSFER (A) TO N", "TRANSFER (A) TO N", "TRANSFER (A) TO N", "TRANSFER (A) TO N", "TRANSFER (A) TO N", "TRANSFER (A) TO N ", "COPY (A) TO N", "COPY (A) TO N", "COPY (A) TO N", "COPY (A) TO N", "COPY (A) TO N", "COPY (A) TO N", "COPY (A) TO N", "COPY (A) TO N ", "ADD (N) TO (A)", "ADD (N) TO (A)", "ADD (N) TO (A)", "ADD (N) TO (A)", "ADD (N) TO (A)", "ADD (N) TO (A)", "ADD (N) TO (A)", "ADD (N) TO (A)", "SUBTRACT (N) FROM (A)", "SUBTRACT (N) FROM (A)", "SUBTRACT (N) FROM (A)", "SUBTRACT (N) FROM (A)", "SUBTRACT (N) FROM (A)", "SUBTRACT (N) FROM (A)", "SUBTRACT (N) FROM (A)", "SUBTRACT (N) FROM (A)", "SELECT (N)", "SELECT (N)", "SELECT (N)", "SELECT (N)", "SELECT (N)", "SELECT (N)", "SELECT (N)", "SELECT (N)", "AUGMENT (N) BY (A)", "AUGMENT (N) BY (A)", "AUGMENT (N) BY (A)", "AUGMENT (N) BY (A)", "AUGMENT (N) BY (A)", "AUGMENT (N) BY (A)", "AUGMENT (N) BY (A)", "AUGMENT (N) BY (A)", "MERGE CONSTANT LENGTH", "MERGE CONSTANT LENGTH", "MERGE CONSTANT LENGTH", "MERGE CONSTANT LENGTH", "MERGE VARIABLE LENGTH", "MERGE VARIABLE LENGTH", "MERGE VARIABLE LENGTH", "MERGE VARIABLE LENGTH", "MULTIPLY UNIFORM RADIX", "MULTIPLY UNIFORM RADIX", "MULTIPLY UNIFORM RADIX", "MULTIPLY UNIFORM RADIX", "MULTIPLY UNIFORM RADIX", "MULTIPLY UNIFORM RADIX", "MULTIPLY UNIFORM RADIX", "MULTIPLY UNIFORM RADIX", "MULTIPLY AND ADD", "MULTIPLY AND ADD", "MULTIPLY AND ADD", "MULTIPLY AND ADD", "MULTIPLY AND ADD", "MULTIPLY AND ADD", "MULTIPLY AND ADD", "MULTIPLY AND ADD", "MULTIPLY AND SUBTRACT", "MULTIPLY AND SUBTRACT", "MULTIPLY AND SUBTRACT", "MULTIPLY AND SUBTRACT", "MULTIPLY AND SUBTRACT", "MULTIPLY AND SUBTRACT", "MULTIPLY AND SUBTRACT", "MULTIPLY AND SUBTRACT", "CONVERT", "CONVERT", "CONVERT", "CONVERT", "CONVERT", "CONVERT", "CONVERT", "CONVERT", "DIVIDE UNIFORM RADIX", "DIVIDE UNIFORM RADIX", "DIVIDE UNIFORM RADIX", "DIVIDE UNIFORM RADIX", "DIVIDE UNIFORM RADIX", "DIVIDE UNIFORM RADIX", "DIVIDE UNIFORM RADIX", "DIVIDE UNIFORM RADIX", "REPLACE SELECTED BITS", "REPLACE SELECTED BITS", "REPLACE SELECTED BITS", "REPLACE SELECTED BITS", "REPLACE SELECTED BITS", "REPLACE SELECTED BITS", "REPLACE SELECTED BITS", "REPLACE SELECTED BITS", "COLLATE AND ADD", "COLLATE AND ADD", "COLLATE AND ADD", "COLLATE AND ADD", "COLLATE AND ADD", "COLLATE AND ADD", "COLLATE AND ADD", "COLLATE AND ADD", "Invalid", "Invalid", "Invalid", "Invalid", "COMPARE", "Invalid", "Invalid", "Invalid", "SPECIAL SELECT", "Invalid", "Invalid", "Invalid", "SPECIAL COPY", "Invalid", "Invalid", "Invalid", "SHIFT LOGICAL", "SHIFT ARITHMETIC", "SCALE NUMERATOR", "SHIFT BINARY", "SHIFT LOGICAL", "SHIFT ARITHMETIC", "SCALE DENOMINATOR", "SHIFT BINARY", "OUTPUT", "INPUT ONE BLOCK/RESET TIMER", "RUN BACK", "RUN FORWARD", "STEP BACK", "REWIND", "UNLOAD", "INPUT FIRST WORD */SET", "ADD FLOATING POINT", "ADD FLOATING POINT", "ADD FLOATING POINT", "ADD FLOATING POINT", "SUBTRACT FLOATING POINT", "SUBTRACT FLOATING POINT", "SUBTRACT FLOATING POINT", "SUBTRACT FLOATING POINT", "TRANSFER FLOATING POINT", "TRANSFER FLOATING POINT", "TRANSFER FLOATING POINT", "TRANSFER FLOATING POINT", "COPY FLOATING POINT", "COPY FLOATING POINT", "COPY FLOATING POINT", "COPY FLOATING POINT", "MULTIPLY FLOATING POINT", "MULTIPLY FLOATING POINT", "MULTIPLY FLOATING POINT", "MULTIPLY FLOATING POINT", "DIVIDE FLOATING POINT", "DIVIDE FLOATING POINT", "DIVIDE FLOATING POINT", "DIVIDE FLOATING POINT", "STEP ON AND TEST", "Invalid", "ENTER MASTER ROUTINE", "SELECT TAG", "COPY INTO TAG", "COPY INTO TAG", "COPY INTO TAG", "COPY INTO TAG", "MODIFY ADDRESS OF NEXT", "MODIFY ADDRESS OF NEXT", "SELECT LITERAL AND DIVISION", "MODIFY ADDRESS OF NEXT", "UNCONDITIONAL JUMP", "SET MODIFICATION GROUP", "MODIFY ADDRESS OF NEXT", "STORE INDICATORS", "STEP ON AND TEST", "STEP ON AND TEST", "STEP ON AND TEST", "STEP ON AND TEST", "SET INDICATORS", "CLEAR INDICATORS", "INTERROGATE INDICATORS", "CONDITIONAL HALT", "ENTER SUBROUTINE", "LEAVE SUBROUTINE", "ENTER PRIORITY CONTROL", "LEAVE MASTER ROUTINE", "TEST ROUTE", "SET MODIFICATION REGISTER", "SET MODIFICATION REGISTER", "SET MODIFICATION REGISTER", "JUMP A = Z ", "JUMP A !=", "JUMP A >= Z", "JUMP A < Z", "JUMP AB = Z", "JUMP AB != Z", "JUMP AB >= Z", "JUMP AB < Z", "BULK COPY SHORT NUMERIC", "BULK COPY SHORT NUMERIC", "UNPACK FIXED FIELD DATA", "UNPACK VARIABLE FIELD DATA", "BULK COPY ALPHA TO SHORT", "BULK COPY LONG NUMERIC", "EDIT FIXED FIELD FORMATS", "CONDENSE", "EDIT FOR HOLLERITH OUTPUT", "EDIT FOR ANELEX OUTPUT", "Invalid", "Invalid", "Invalid", "Invalid", "Invalid", "Invalid", "TRANSFER DOUBLE LENGTH", "TRANSFER DOUBLE LENGTH", "TRANSFER DOUBLE LENGTH", "TRANSFER DOUBLE LENGTH", "COPY DOUBLE LENGTH", "COPY DOUBLE LENGTH", "COPY DOUBLE LENGTH", "COPY DOUBLE LENGTH", "ADD DOUBLE LENGTH", "ADD DOUBLE LENGTH", "ADD DOUBLE LENGTH", "ADD DOUBLE LENGTH", "SUBTRACT DOUBLE LENGTH", "SUBTRACT DOUBLE LENGTH", "SUBTRACT DOUBLE LENGTH ", "SUBTRACT DOUBLE LENGTH"}; unsigned char buff[1000]; void iosum(); // print summary of I/O activity void opscometc(); // analyse user instructions in buff, such as set indicator, or mount tapes, etc void readkbd(int mode); // read keyboard input and echo if necessary int get_connected(char *target_thost, unsigned short target_tport); // connect to remote host for driving ops console char *indregprompt = "%5d Set indicator register (currently %5X, mask %5X)\n"; char *echofmt = NULL; // format used for echoing console input char *blkfmt = "%5d T/W: %s\n"; char *redfmt = "%5d !!!: %s\n"; // operator error messages char *cntfmt = "\n : "; // prefix for continuation lines int rhpad = 0; // set to RH margin with VT100 emulation char *opscons = NULL; // connect ops console to this TCP/IP address int opsock = -1; // socket for communication with ops console char *netprint = NULL; // connect printer to this TCP/IP address int lpsock = -1; // socket for communication with printer void illegal(char *s, int val) // stop the machine with diagnostic on detection of illegal instruction { int saddr = nnn & 32766; // round to long compartment address and avoid bad mem ref printf("%d Illegal: %s after %d instructions\n", sc-1, s, count); fprintf(diag, "Illegal: %s [%06X %d %o] after %d instructions\n", s, val, val, val, count); sc --; // back step to failing instruction fprintf(diag, "SC = %d (%d,%d) instr = %d/%d/%d %d\n", sc, sc&8191, sc-lastoverlay, instr>>16, (instr>>15)&1, (instr>>13)&3, instr&017777); fprintf(diag, " A = %06X %05X [%d %d]\n", msa, lsa, msa, lsa); fprintf(diag, " B = %06X %05X [%d %d]\n", msb, lsb, msb, lsb); fprintf(diag, " N = %04X [%d] (%d) = %06X (%d) = %06X\n", nnn, nnn, saddr, store[saddr], saddr+1, store[saddr+1]); fprintf(diag, " D = %06X %05X [%d %d]\n", msd, lsd, msd, lsd); fprintf(diag, " I = %06X\n", indreg); buff[0] = '0'; buff[1] = 0; while ( buff[0] != 'x' ) { opscometc(); // first command just reads 0 zero into indreg fflush(diag); fprintf(stderr, "Reply x to terminate\n"); readkbd(1); } iosum(); exit(1); } char charval[256]; // Leo III char value index on UK ASCII value char hexval[256]; // TEMP !!! or maybe not // Basic Quartet // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // ___________________________________________________ // 0 | Sp | // 1 | - | // 2 | & | // Control 3 | 0 | // Quartet 4 | 1 2 3 4 5 6 7 8 9 10 11 + : . £ | // 5 | A B C D E F G H I = * ' ? ? ? | // 6 | J K L M N O P Q R ? % > < ½ \ | // 7 | / S T U V W X Y Z ( ) , ; ? ? | // |_________________________________________________| void mkchars() // create the above conversion tables charval and hexval // same as in Translator // hexval may never get used { int i; memset(charval, 0, 255); memset(hexval, 0, 255); for ( i = 0; i<16; i++ ) { hexval[i] = i; hexval[i+'0'] = i; } for ( i = 0; i<6; i++ ) hexval[i+'A'] = i + 10; hexval['t'] = 10; // ten char charval['t'] = 0x4A; // ten char hexval['e'] = 11; // eleven char charval['e'] = 0x4B; // eleven char hexval['p'] = 15; // pound char charval['p'] = 0x4F; // pound char charval['g'] = 0x6A; // goto, i.e. right pointing arrow charval[' '] = 0; charval['-'] = 16; charval['&'] = 32; charval['0'] = 48; for ( i = 1; i<10; i++ ) { charval[i+'0'] = i + 0x40; charval[i+'A'-1] = i + 0x50; charval[i+'Q'] = i + 0x70; // must precede next line charval[i+'I'] = i + 0x60; } charval['/'] = 0x71; hexval['+'] = 12; charval['+'] = 0x4C; hexval[':'] = 13; charval[':'] = 0x4D; hexval['.'] = 14; charval['.'] = 0x4E; hexval['£'&255] = 15; charval['£'&255] = 0x4F; // pound char charval['='] = 0x5A; charval['*'] = 0x5B; charval['\''] = 0x5C; charval['?'] = 0x5D; charval['%'] = 0x6B; charval['>'] = 0x6C; charval['<'] = 0x6D; charval['\\'] = 0x6F; charval['h'] = 0x6E; // h = half charval['('] = 0x7A; charval[')'] = 0x7B; charval[','] = 0x7C; charval[';'] = 0x7D; charval['\n'] = LINEEND; } char alpha[256]; // convert Leo octet to ASCII char void mkalpha() // make the inverse tables converting Leo III code to ASCII { int i; memset(alpha, '!', 256); // exclamation mark is not in the Leo char set for ( i = 0; i<128; i++ ) if ( charval[i] > 0 ) { alpha[charval[i]] = i; alpha[charval[i]&0x3F] = i; // create the alpha sextets } alpha[0x4F] = '£'; alpha[0xF] = '£'; alpha[LINEEND] = '\n'; alpha[LINEEND&0x3F] = '\n'; alpha[BLOCKEND] = '#'; alpha[BLOCKEND&0x3F] = '#'; alpha[0] = ' '; } char *tostr(int ms, int ls) // convert a long word of alpha to a string // result alternates between two strings // can be used only once or twice in a printf { static unsigned char *res = buff + 990; int i; if ( res == buff + 990 ) res -= 10; else res = buff + 990; ms &= 0xFFFFF; // ignore sign for ( i = 0; i<5; i++ ) { res[i] = alpha[ms>>12]; ms = ((ms<<8) | (ls>>12)) & 0xFFFFF; ls = (ls<<8) & 0xFFFFF; } res[5] = 0; return (char *)res; } int crash(int a, int b) // forcibly die diagnostically { char *zz, nogo; zz = NULL; nogo = *zz; // deliberately fail return 0; // to pacify the compiler } int qcry(int a, int b) // evaluates the carry digits out of each quartet on // addition of a and b (each 20 bits) // no good -- does not propagate carries properly // still has some temporary diagnostic value { int al = a & 0x77777; // just the bottom 3 bits of each quartet int bl = b & 0x77777; // just the bottom 3 bits of each quartet int at = a & 0x88888; // just the top bit of each quartet int bt = b & 0x88888; // just the top bit of each quartet int sc = (al + bl); // no carying out of a quartet is possible here sc &= 0xCCCCC; // clear space for carry digits in next quartets sc += at + bt; // carries now propagated into bottom of next quartet return sc & 0x111110; // just pick out the carry digits } int add5(int a, int d, int radix) // add using radix // carry digits are evaluated as the difference between addition and exclusive or { int aa = a + radix; // add in exces digits int r = aa + d; // raw addition int c = ((aa ^ d) ^ r) & 0x111110; // carry digits -- only OK if no digits are out of radix // maybe | ((a ^ radix) ^ aa) & 0x111110; // add in carries that will have happened if the data is out of radix int mask = (c>>1) | (c>>2) | (c>>3) | (c>>4); // fprintf(diag, "\n a = %06X d = %06X\n", a, d); // fprintf(diag, "aa = %06X c = %06X m = %05X\n", aa, c, mask); // fprintf(diag, " r = %06X\n", r); r -= radix - (radix&mask); // removes excess for those digits where there was no carry if ( verbosity >= 9 ) { if ( qcry(a, radix) != 0 ) fprintf(diag, "%5d Out of radix operand %06X\n", sc-1, a); if ( qcry(d, radix) != 0 ) fprintf(diag, "%5d Out of radix operand %06X\n", sc-1, d); } // fprintf(diag, "result = %05X\n", r); return r & 0x1FFFFF; // result is 21 bits with carry at the top } void complementd() // if D is -ve then change form between sign-and-modulus and complement // The transformation algorithm is the same for either direction { int magic; // fprintf(diag, "msd = %06X lsd = %06X\n", msd, lsd); if ( (msd&0x100000) == 0 ) // positive number, no need to change anything return; if ( verbosity >= 9 ) { if ( qcry(lsd, creg) != 0 || qcry(msd&0xFFFFF, topcreg) != 0 ) fprintf(diag, "%5d Out of radix data %06X %05X\n", sc-1, msd, lsd); } magic = (0xFFFFF - creg); lsd = (magic - lsd) & 0x1FFFFF; // fprintf(diag, "lsd = %06X magic = %06X\n", lsd, magic); magic = (0xFFFFF - topcreg); msd = (magic - msd) & 0x1FFFFF; // fprintf(diag, "msd = %06X magic = %06X\n", msd, magic); lsd = add5(lsd, 1, creg); // fprintf(diag, "lsd = %06X after add\n", lsd); if ( (lsd&0x100000) != 0 ) //carried { msd = add5(msd, 1, topcreg) & 0x1FFFFF; // and needed in case input was -0 lsd &= 0xFFFFF; } // fprintf(diag, "msd = %06X lsd = %06X\n", msd, lsd); } void loadD(int disc, int addr) // loads either long or short value into D // leaves in sign-and-modulus form { if ( addr < 0 || addr >= 32768 ) illegal("Bad memory address", addr); lsd = store[addr]; if ( disc == 0 ) // short form msd = lsd & 0x100000; // take sign bit to top half else // long form msd = store[addr+1]; lsd &= 0xFFFFF; // and remove sign digit from bottom half } void notImplemented(char *s) { sprintf(buff, "Not implemented: %s ", s); if ( memcmp(s, "Long ", 5) == 0 ) // Long form instruction does not give its name strcat(buff, ccstr[instr>>13]); // So pick up name from table illegal(buff, instr>>13); } void showIndicators(int opcode) // display indicator lights -- somehow or other // opcode = 1 display the indicators and send back any waiting command // opcode = 2 as for 1, but send command zz if there is no waiting command and we are in turbo mode // opcode = 0 just display the indicators // opcode = 3 as for 1 but with a request to load a tape { static int previous = -1; // guaranteed not to match first time if ( ((indreg ^ previous) & 017777) == 0 && opcode == 0 ) return; // omit testing top 2 bits which sometimes change frequently if ( opsock < 0 ) // no emulated ops console window { fprintf(stderr, "Indicator now %04X (hex)\n", indreg); // TEMP !!! previous = indreg; } else // ops console emulated by LeoConsole.java {//memset(buff, 0, 8); buff[1] = indreg>>8; buff[2] = indreg&0377; buff[0] = opcode; #ifndef WIN32 write(opsock, buff, 8); #else send(opsock, buff, 8, 0); // MS does not do proper sockets #endif buff[3] = 0; // cancel any display message // flush(opsock); // do we need to force output? } previous = indreg; } int bmask[] = { 0x00000, 0x0000F, 0x000F0, 0x000FF, 0x00F00, 0x00F0F, 0x00FF0, 0x00FFF, 0x0F000, 0x0F00F, 0x0F0F0, 0x0F0FF, 0x0FF00, 0x0FF0F, 0x0FFF0, 0x0FFFF, 0xF0000, 0xF000F, 0xF00F0, 0xF00FF, 0xF0F00, 0xF0F0F, 0xF0FF0, 0xF0FFF, 0xFF000, 0xFF00F, 0xFF0F0, 0xFF0FF, 0xFFF00, 0xFFF0F, 0xFFFF0, 0xFFFFF }; // this mask is used for expanding a bit pattern to equivalent quartets void fetchAlpha(int p) // fetch long word as alpha value // N.B. p is the address of the MS half // result is in msd, lsd { int lsw = store[p-1]; // least significant half Q1-5 int msw = store[p]; // most significant half Q6-10 lsd = ((lsw>>8)&0xF00) | ((lsw>>4)&0xF0) | (lsw&0xF) | ((msw<<8)&0xF000) | ((msw<<4)&0xF0000); if ( (msw&0x30000) == 0x10000 ) // negative value msd = 0x100000; else // positive or zero msd = 0; } void storeAlpha(int p) // store long word as alpha value // N.B. p is the address of the MS half // value to be stored is in lsd { int i; char q[11]; // ignore q[0] so it looks like the documentation sect 5.4 memset(q, 0, 11); // zeroise to set top digits for ( i = 1; i<10; i+=2 ) // count 1,3,5,7,9 { if ((q[i] = lsd & 0xF) != 0 ) q[i+1] = 4; lsd = lsd >> 4; } if ( msd >= 0x100000 ) // if sign digit set q[10] ++; // is this right? lsd = 0; // least significant half Q1-5 msd = 0; // most significant half Q6-10 for ( i = 5; i>0; i-- ) { lsd = (lsd<<4) | q[i]; msd = (msd<<4) | q[i+5]; } store[p-1] = lsd; // least significant half Q1-5 store[p] = msd; // most significant half Q6-10 } void storeDecimal(int val, int p) // Store number as decimal characters // used for mag tape block counts // value to be stored is in val, must be positive { char d[5]; int q, r; int i = 0; memset(d, 0, 5); // zeroise to set top digits d[0] = 0x30; // Leo III zero while ( val != 0 ) { q = val/10; r = val - 10*q; if ( r == 0 ) d[i++] = 0x30; else d[i++] = r + 0x40; val = q; } store[p--] = (d[4]<<12) | (d[3]<<4) | (d[2]>>4); // most significant half store[p] = ((d[2]&15)<<16) | (d[1]<<8) | d[0]; // least significant half } FILE *ptr = NULL; // paper tape reader FILE *ptr0 = NULL; // paper tape reader from which #include was read char *ptr_fn = "ptr.txt"; // name of file -- can be changed on command line void svc(int nnn) // implement master routine action ENTER PRIORITY CONTROL // only implements those calls found in the Intercode Translator { if ( nnn == 1 ) // operator query { fprintf(diag, "ALARM *%02X\n", lsa); if ( diag == stdout ) // stdout probably redirected, use std err fprintf(stderr, "ALARM *%02X for operator\n", lsa); else printf("ALARM *%02X for operator\n", lsa); fflush(diag); // so that the file can be seen in another window readkbd(1); msa = atoi(buff) - 1; if ( msa < 0 || msa >= 9 ) notImplemented("Reply out of range"); // TEMP sc = store[sc + msa]; fprintf(diag, "Number read was %d, jump to %d\n", msa+1, sc); msa = msb = lsa = lsb = 0; } else if ( nnn == 2 ) // unload program(me) { printf("PROGRAMME UNLOADED: ADDRESS = %d after %d instructions\n", sc, count); if ( diag != stdout ) { fprintf(diag, "PROGRAMME UNLOADED: ADDRESS = %d after %d instructions\n", sc, count); if ( verbosity > 0 ) iosum(); fclose(diag); printf("**********************************\n", sc); // will this appear? } exit(0); } else { sprintf(buff+500, "ENTER PRIORITY CONTROL %d", nnn); notImplemented(buff+500); } } // Mag tape format (interim?) // Each block is in the form of integers being the store contents (may need to remove sign bits) // at the start of each block is an integer giving the number of integers in the data block itself // each device has table entries indexed on the route number -- often called i for historic reasons // variable auto indicates whether I/O is autonomous // Buffer swapping can be inhibited with -b switch, occasionally useful diagnostically // Mag tapes are special because of RUN and STEP operations // Until we have proper master routine (MR) code, the routine master() below, // simulates those MR facilities needed for running the Intercode Translator // dvstat records the status of each device esp. tape // 0 = normal. Reads or writes take place without any preceeding lseek() // 1 = post run. Read needs a back lseek() so as to read the block most recently read // -1 = set to manual // mtblk records the size of the block most recently read or written // This enables a call of lseek() to back step the current block. // Each block has the size of the preceeding block in the header word, shifted up 11 bits // max block size is 1000 long words -- i.e. 2000 ints // int filenum[64]; // maps route number to file number int dvdv[] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // device number from open() routine -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2}; int dvfn[] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // filename of tape (or other device) as 2 Leo chars - set by open -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2}; int dvty[] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // device type of this file -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2}; int mtmode[] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, // 0 = write, 1 = read -- same as Leo -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-2}; int mtblk[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // size of block just behind the tape head 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2}; int mtcnt[] = {6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, // number of block just behind the tape head 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,-2}; int dvstat[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // state of each tape device -- see above 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2}; int dveng[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // device is engaged until this value of count 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,-2}; int endmark[] = {8, 0, 0, 0xF6F6F, 0x6F6F6, 0x55972, 0x56596, 0x7E, 0x7E000 }; // int endmark[] = {8, 0, 0, 0xF6F6F, 0x6F6F6, 0x50000, 0x56596, 0x7E, 0x7E000 }; // should it be FIN not FINIS? int endmark2[] = {0x4008, 0xF7F43, 0x7F7F7, 0xF6F6F, 0x6F6F6, 0x48, 0, 0x7E, 0x7E000 }; int endmark3[] = {0x4008, 0x5547B, 0x7A556, 0x5547B, 0x7A556, 0x48, 0, 0x7E, 0x7E000 }; char *leofn(char *pref, int r) // returns the file name to use for route r // pref indicates the type of device (e.g. MT for mag tape // result is actually returned in buff { int fn = dvfn[r]; char *ext = "leo"; if ( pref[0] != 'M' ) // not a mag tape ext = "txt"; // must be some sort of text file if ( fn < 0 ) // never set by Intercode action 40 (should never happen???) sprintf(buff, "%s%d.%s", pref, r, ext); // old form of file name else sprintf(buff, "%s-%c%c.%s", pref, alpha[fn>>8], alpha[fn&0xFF], ext); return buff; } void backread(int i, int addr) // reads the block just behind the tape head, i.e. often that last read. // the data is stored at store[address] onwards, and the header word with // The tape is left positioned as it was on entry // This may be incorrect -- seems to seek 1 word too few { int sz = mtblk[i]; int w; if ( verbosity >= 9 ) fprintf(diag, "Step back route %02o, block size %d\n", i, sz); if ( sz == 0 ) illegal("Read backwards at start of tape", i); if ( (w = lseek(dvdv[i], -(int)(sz * sizeof(int)), SEEK_CUR)) < 0 ) { perror("Tape"); illegal("lseek error report on backread", i); } read(dvdv[i], store+addr, sz * sizeof(int)); // fprintf(diag, "Reading MT %d (size %d) at offset %d into address %d, align word (%d) = %06X %05X, index word (%d) = %06X %05X\n", // i, sz, w, addr, addr+4, store[addr+5], store[addr+4], addr+10, store[addr+11], store[addr+10]); dvstat[i] = 1; } void backstep(int i, int addr) // The tape position is moved back one block // The header word is returned as the result // addr is a place where we can read an int (often the annex) { int sz = mtblk[i]; int w; if ( verbosity >= 9 ) fprintf(diag, "Step back route %02o, block size %d\n", i, sz); if ( sz == 0 ) illegal("Step backwards at start of tape", i); if ( (w = lseek(dvdv[i], -(int)((sz+1) * sizeof(int)), SEEK_CUR)) < 0 ) { perror("Tape"); illegal("lseek error report on backstep", i); } read(dvdv[i], store+addr, sizeof(int)); // just read the header word mtblk[i] = store[addr]>>11; // size of block in front of read head dvstat[i] = 0; // now read and write address the same block lseek(dvdv[i], -(int)sizeof(int), SEEK_CUR); // back over the header word mtcnt[i] --; // block counter needs to move back one // fprintf(diag, "Now after block size %d - current should be %d, position is %d\n", store[addr]>>11, store[addr]&0x7FF, w); } int alignmark(int i, int addr) // returns 0 if the data at addr onwards is an alignment block // Block end word = 0x7E, 0x7E000 // 5 alignment marks = 0xF6F6F, 0x6F6F6 { int w0 = store[addr++] & 0xF3F3F; int w1 = store[addr++] & 0x3F3F3; int res = 1; while ( w0 != 0x3E || w1 != 0x3E000 ) // continue to end of block if ( (w0|0x0003F) == 0xF2F3F && w1 == 0x2F2F2 // four alignment marks at start of long word || w0 == 0xF2F2F && (w1|0x3F000) == 0x3F2F2 ) // four alignment marks at end of long word { w0 = 0x3E; w1 = 0x3E000; // force termination res = 0; } else { w0 = store[addr++] & 0xF3F3F; w1 = store[addr++] & 0x3F3F3; } return res; } void terminate(int i) // terminate the tape i (i.e. indexed in the above tables) { int dv = dvdv[i]; mtmode[i] = -1; // indicate mode unknown endmark[0] = (mtblk[i]<<11) + 8; msd = 0; lsd = ++(mtcnt[i]); // put in master's block count storeDecimal(lsd, 141); // using master's private store endmark[1] = store[140]; endmark[2] = store[141]; write(dv, endmark, sizeof(endmark)); write(dv, endmark2, sizeof(endmark2)); write(dv, endmark3, sizeof(endmark3)); mtblk[i] = 8; // size of terminator block // if ( verbosity >= 1 ) fprintf(diag, "%5d Terminated MT on route %02o on UNIX device %d\n", sc-1, i, dv); } void setengaged(int r, int len) // sets device engages, len may eventually indicate length of time { if ( ioauto == 0 ) // not doing autonomous peripherals return; if ( r == 58 || ioauto > 1 ) // temp just the typewriter with -i1 dveng[r] = count + 200; // temp all devices stay engaged for only 200 instructions } void dooverlay() { int i = lsa - 2; // overlay number int w3 = store[3]; // pointer to extra chapter if ( msa == 0 && msb == 0 && lsa < 10 && lsb < 3 ) // these are the only ones we have seen { fprintf(diag, "\nEntering overlay %d, starts at %d - %ld short words - after %d instructions\n", i, oladdr[i], olsize[i]/sizeof(int), count); fprintf(diag, "A = %06X %05X B = %06X %05X w3 = %d X20 = %d %d %d %d\n", msa, lsa, msb, lsb, w3, store[w3+20], store[w3+21], store[w3+22], store[w3+23]); if ( oladdr[i] != store[w3+20] && oladdr[i] != store[w3+22] ) notImplemented("This overlay - memory mismatch"); lastoverlay = oladdr[i]; // used in diagnostics memcpy(store + oladdr[i], overlay[i], olsize[i]); // preserved overlay copied back to true location } else notImplemented("This overlay - unknown number"); } /* --------------------------------- Device types are 1 7-hole PT output, in a 6-bit code with odd parity inserted by the hardware in a fixed position (see 13.5) 2 Non-standard 80-column card output (see 12.5) 3 Standard (decimal) 80-column card output (see 13.3) 4 Anelex printer on Standard Assembler (see 13.6, 14.1) 5 5 or 7-hole PT input (see 12.7, 13.4) 6 80-column (decimal) card input 7 Magnetic tape, first Assembler (see 13.1) 8 Magnetic tape, second Assembler (behaves as 7 if only 1 assembler) 9 Anelex printer on special Assembler (see 13.6) 10 80-column non-standard card input (see 12.6) 11 Magnetic tape, third Assembler (behaves as 7 if only 1 or 2 assemblers) 12 Autolector (see 12.11) 13 40-column card input (Uptime reader) (see 12.12) 14 Special form of dual printer (see 19) for software use. All file control orders must be in computer code. 15 Dual input (PT or cards) (see 12.9) 16 Non-standard PT Output (see 12.8) 17 Cheque Sorter (see 12.13) 18 Non-standard 40-column card input (see 12.12) 19 Dual printer i.e. special or standard assembler (see 12.10) 20 Data Transmission Link input (see 12.14) 22 Data Transmission Link output (see 12.15) Amendment No.40 --------------------------------- */ int g4mark[1281]; int endcount = 10; // number times to put an extra endmark when read off end of tape int readendMT(int i, int k) // deal with reading off end of tape on mt dev i into store starting at k { int fn; if ( --endcount < 0 ) illegal("Read off end of tape", lsa); memcpy(store+k, endmark+1, 8*sizeof(int)); // supply a FINISH block, but not for ever fn = dvfn[i]; // do we need to worry about block count? fprintf(diag, "forcing FINISH block on %c%c\n", alpha[fn>>8], alpha[fn&0xFF]); return endmark[0] & 0x7FF; // bottom 11 bits } void iosum() // print summary of I/O activity { int i, msa; return; // suppress this for now fprintf(diag, "\nI/O summary\n"); for ( i = 0; i<20; i++ ) for ( msa = 0; msa<64; msa++ ) if ( g4mark[i*64 + msa] != 0 ) fprintf(diag, "G4act type %2d Q6 = %3d repeat %5d\n", i, msa, g4mark[i*64 + msa]); } // mode info -- a query indicates confident prediction, but not actually seen in execution // 0x0F = open PTR more info in B Q6-7 = file name (line 14870 - uncommented) // 0x19 = close PTR // 0x05 = read PTR // 0x18 = open printer, comment = "OPEN PRINTER OR MT A9" // 0x22 = close printer // 0x04 = print on Anelex // 0x1A = close CR ? // 0x06 = read CR ? // 0x10 = open CR ? // sometimes Q1-5 of B hold address of transit area int ptroute = -1; // route upon which paper tape was normally read char *readptrline() // read one line of PTR data and strip comments // A line starting with [ as the very first character is ignored // A [ anywhere else causes everything to the right of it so be seen as comment // and spaces on its left to be removed // Thus if a line needs to end with a space, it cannot have a comment // The parameter is there in case of future enhancements with multiple paper tape readers { int j, i; char *res; buff[0] = '['; // next skip whole line comments while ( buff[0] == '[' && (res = fgets(buff, 999, ptr)) != NULL ) ; if ( res == NULL && ptr0 != NULL ) // end of included file { fclose(ptr); ptr = ptr0; ptr0 = NULL; return readptrline(); // continue with the next line of input from the top level file } j = strlen(buff); while ( --j >= 0 && buff[j] < ' ' ) ; buff[++j] = 0; // remove trailing CRLF - works on PC or UNIX i = -1; while ( ++i < j && buff[i] != '[' ) ; // look for start of comment if ( i != j ) // comment found { while ( --i >= 0 && buff[i] <= ' ' ) ; buff[i+1] = 0; // chop off white space ahead of the [ } if ( memcmp(buff, "#include", 8) == 0 ) { if ( ptr0 != NULL ) // can only do one level illegal("Cannot include from within an included file", 0); ptr0 = ptr; ptr = fopen(buff+9, "r"); if ( ptr == NULL ) // can only do one level { perror(buff); illegal("Cannot open included file", 0); } return readptrline(); // continue with the first line of input from the included file } return res; } void readptr(int r) // read paper tape on route r in master mode { int i, j, k, w; char c; ptroute = r; // route upon which paper tape is normally read while ( ptr == NULL ) // keep trying until we get a reader, or run is abandonned { printf("Need paper tape on route %02o\n", r); buff[3] = 'P'; // P for paper tape -- shows need for tape mount buff[4] = r; readkbd(3); // give chance for a p command opscometc(); // keep going until abandoned by x or paper tape file is opened } if ( readptrline(ptr) == NULL ) dvstat[r] = -1; // read off end of tape -- set unloaded if ( verbosity >= 1 ) { fprintf(diag, "%5d Reading PT route %02o into location %d - after %d instructions\n", sc-1, r, store[r+64]&077777, count); fprintf(diag, "Data read was == %s (19 action - %d instructions)\n", (char *)buff, count); } i = -1; j = 0; while ( (c = buff[++i]) != 0 ) if ( (c = charval[c&255]) >= 0 ) buff[j++] = c; buff[j++] = BLOCKEND; // should this be line end or block end? buff[j] = 0; buff[j+1] = 0; // should these be zero? yes?? buff[j+2] = 0; buff[j+3] = 0; // to guarantee a full word i = store[r+64] & 0x7FFF; // address of transfer area k = 0; while ( k < j ) // 5 chars per long compartment { w = (buff[k]<<12) | (buff[k+1]<<4) | (buff[k+2]>>4); store[i++] = ((buff[k+2]<<16) | (buff[k+3]<<8) | buff[k+4]) & 0xFFFFF; store[i++] = w; k += 5; } // store[i+1] = 0x7E7E7; store[i] = 0xE7E7E; // unnecessary ?? -- or even unwise if ( verbosity >= 1 ) { i = store[r+64] & 0x7FFF; // address of transfer area fprintf(diag, "Stored %05X %05X at %d\n", store[i], store[i+1], i); fprintf(diag, "Big print %05X %05X - %05X %05X - %05X %05X - %05X %05X - %05X %05X - %05X %05X\n", store[i+1], store[i], store[i+3], store[i+2], store[i+5], store[i+4], store[i+7], store[i+6], store[i+9], store[i+8], store[i+11], store[i+12]); } } void writelp(int k, int dv) // write printer output from location k onwards onto UNIX device dv { int i = 0; int j; unsigned char c; while ( ++i < 33 ) // ignore the control word for now { int msw = store[k+i+i+1]; int lsw = store[k+i+i]; strcpy(buff + i*5 - 5, tostr(msw, lsw)); for ( j = 5; j>0; j-- ) if ( (c = buff[i*5 - j]) == '#' ) // blockend { buff[i*5 - j] = 0; // mark end of printing if ( verbosity >= 9 ) fprintf(diag, "Stopped at i = %d, j = %d %06X %05X\n", i, j, msw, lsw); i = 8192+i; // sure to stop the loops j = -1; } // else if ( c == '\n' ) // lineend // { fprintf(diag, "i = %d, %06X %05X\n", i, msw, lsw); // notImplemented("Multi-line printing"); // } } if ( verbosity >= 9 ) // only show this when really desperate fprintf(diag, " Stopped %d %d\n", i, i&8191); i &= 8191; buff[k = i*5 - 5] = 0; if ( verbosity >= 1 ) fprintf(diag, " Printed %d %ld:[%s]\n", k, strlen(buff), (char *)buff); k = strlen(buff); while ( --k > 0 && buff[k] == ' ' ) ; strcpy(buff + ++k, "\n"); // should we think of CRLF for Microsoft #ifndef WIN32 write(dv, buff, k+1); // write to the printer file or java printer #else if (lpsock < 0) { write(dv, buff, k + 1); // write to the printer file _flushall(); } else send(dv, buff, k+1, 0); // write to the java printer #endif } void skipheaders(int route, char *mode, int annex) // check for header blocks on newly opened tape file // and skip past them if they are there // annex is the address of the relevant annex // The global variable buff contains the name of the file being opened // Coded as per http://sw.ccs.bcs.org/DAVID-H/LeoMan/VolIV3.txt section D.4.2 { int c; int hdrs = 0; char tsn[6]; fprintf(diag, "MT route %02o opened for %s on UNIX device %d - %s\n", route, mode, dvdv[route], (char *)buff); c = read(dvdv[route], store+annex, 9*sizeof(int)); // read our header and 4 long words of the data tsn[0] = '0' + ((store[annex+8]>>12)&15); tsn[1] = '0' + ((store[annex+8]>>4)&15); tsn[2] = '0' + ((store[annex+7]>>16)&15); tsn[3] = '0' + ((store[annex+7]>>8)&15); tsn[4] = '0' + (store[annex+7]&15); tsn[5] = 0; if ( c <= 0 || (store[annex+1]&0x3F3F) != 1 ) // not block number 1 at start of file { hdrs = 0; if ( c < 0 ) perror(buff); fprintf(diag, "No headers on tape file: %s (read returns %d)\n", (char *)buff, c); } else if ( mode[0] == 'r' ) { fprintf(diag, "Reading master compatible tape %s file: %s\n", tsn, (char *)buff); hdrs = 87*sizeof(int); // 6 * 11 + 21 = position after header blocks } else { fprintf(diag, "Writing master compatible tape %s file: %s\n", tsn, (char *)buff); lseek(dvdv[route], 66*sizeof(int), SEEK_SET); // position on block 7 store[annex] = 0x5014; store[annex+1] = 0x47; store[annex+2] = 0; store[annex+3] = 0; store[annex+4] = 0; store[annex+5] = 0x47; store[annex+6] = 0x1E000; // mystery word store[annex+7] = dvfn[route]; store[annex+8] = 0; // file identity store[annex+9] = 0x41; store[annex+10] = 0; // run and rerun number store[annex+11] = 0x41; store[annex+12] = 0; // reel no set to 1 store[annex+13] = store[138]; store[annex+14] = store[139]; // date store[annex+15] = ((prognum[2]&15)<<16) + (charval[prognum[3]]<<8) + charval[prognum[4]]; store[annex+16] = (charval[prognum[0]]<<12) + (charval[prognum[1]]<<4) + (charval[prognum[2]]>>4); store[annex+17] = 0; store[annex+18] = 0; // 0 for 45K store[annex+19] = 0x7E; store[annex+20] = 0x7E000; // block end word write(dvdv[route], store+annex, 21*sizeof(int)); // programme header block hdrs = 87*sizeof(int); // 6 * 9 + 17 = position after header blocks } if ( lseek(dvdv[route], hdrs, SEEK_SET) < 0 ) // position at the beginning of user data perror(buff); // and complain if it fails if ( mode[0] == 'w' ) // writing ftruncate(dvdv[route], hdrs); // chop off any data about to be overwritten } void sex2oct(int k, int sw) // convert sextets back to octet form after reading fomr a mag tape file // Tapes are written just by transferring the data from the store as is, // and so will usually have extra bits that a real tape deck would not write on the tape. // These bits need removing on read back and the correct control quartet created from the duet // sw is the number of short words to be converted, start at address k { int i; int msa, lsa; // use local values as action 19 do not alter A for ( i = 0; i 1280 || msa >= 64 ) fprintf(diag, "G4act type %2d Q6 = %3d route %2d loc %5d\n", dvtype, msa, lsa, sc-1); else g4mark[j] ++; g4mark[0] ++; if ( dvtype == 8 ) // ignore alternate controller matters dvtype = 7; // (don't really understand the conventions here) if ( dvtype == 14 || dvtype == 19 ) // dual printers are a bit mysterious dvtype = 9; // (don't really understand the conventions here) if ( dvtype == 5 ) // paper tape reader { if ( msa == 15 ) // open paper tape reader { if ( ptr == NULL ) // any subsequent open carries on where we left off ptr = fopen(ptr_fn, "r"); if ( ptr == NULL ) { perror(ptr_fn); illegal("Cannot open paper tape reader file", lsa); } fprintf(diag, "%5d paper tape reader %c%c opened reading %s route %02o ==[%d] after %d instructions\n", sc-1, alpha[msb>>8], alpha[msb&0xFF], ptr_fn, lsa, msa, count); dvfn[i] = msb; // remember the filename for later - may not ever be used msb = dvtype; } else if ( msa == 5 ) // read on paper tape reader { char *test = readptrline(ptr); if ( verbosity >= 1 ) { fprintf(diag, "Reading route %02o (pseudo = %d) into location %d\n", lsa, lsb, store[lsa+64]); fprintf(diag, "Data address %d (pseudo %d)\n", store[lsa+64], store[lsb+64]); fprintf(diag, "Data read was == %s (MR call - %d instructions)\n", (char *)buff, count); } if ( test == NULL ) illegal("Read off end of paper tape", 0); i = -1; j = 0; while ( (c = buff[++i]) != 0 ) if ( (c = charval[c&255]) >= 0 ) buff[j++] = c; buff[j++] = BLOCKEND; // should this be line end or block end? buff[j] = 0; buff[j+1] = 0; // should these be zero? yes?? buff[j+2] = 0; buff[j+3] = 0; // to guarantee a full word if ( buffswap != 0 ) // if implementing double buffering in master { msa = store[lsa+64]; // swapping route and pseudo-route -- this is route msb = store[lsb+64]; // this is pseudo-route store[lsa+64] = msb; store[lsb+64] = msa; // now swapped over } i = store[lsb+64]; // address of transfer area k = 0; while ( k < j ) // 5 chars per long compartment { msa = (buff[k]<<12) | (buff[k+1]<<4) | (buff[k+2]>>4); lsa = ((buff[k+2]<<16) | (buff[k+3]<<8) | buff[k+4]) & 0xFFFFF; store[i++] = lsa; store[i++] = msa; k += 5; } // store[i+1] = 0x7E7E7; store[i] = 0xE7E7E; // unnecessary ?? -- or even unwise i = store[lsb+64]; // address of transfer area if ( verbosity >= 1 ) { fprintf(diag, "Stored %05X %05X at %d\n", store[i], store[i+1], i); fprintf(diag, "Big print %05X %05X - %05X %05X - %05X %05X - %05X %05X - %05X %05X - %05X %05X\n", store[i+1], store[i], store[i+3], store[i+2], store[i+5], store[i+4], store[i+7], store[i+6], store[i+9], store[i+8], store[i+11], store[i+12]); } } else if ( msa == 25 ) // close paper tape reader { fprintf(diag, "Closing paper tape reader\n"); // fclose(ptr); // don't actually close the file } else bad = 1; } else if ( dvtype == 7 ) // magnetic tape { if ( msa == 18 || msa == 17 ) // open magnetic tape file for writing or reading { mtmode[i] = 18 - msa; // writing indicated by 0 on Leo III, reading indicated by 1 fprintf(diag, "%5d FILE %c%c opened mode %d route %02o ==[%d] after %d instructions\n", sc-1, alpha[msb>>8], alpha[msb&0xFF], mtmode[i], lsa, msa, count); if ( dvfn[i] != msb ) // check the filename for later use on open fprintf(diag, " Filename mismatch %05X different from %05X\n", dvfn[i], msb); } else if ( msa == 7 ) // magnetic tape input via intercode 42 { if ( verbosity >= 1 ) { fprintf(diag, "%5d FILE input route %02o action %d after %d instructions\n", sc-1, lsa, msa, count); fprintf(diag, "Data address %d (pseudo %d)\n", store[lsa+64], store[lsb+64]); } if ( mtmode[i] != 1 && verbosity > 0 ) fprintf(diag, "Warning: Reading on an output file - mode = %d\n", mtmode[i]); if ( dvstat[i] > 0 ) // MT read after RUN BACK or RUN ON { dvstat[i] = 0; backstep(i, 140); // data is actually in the annex, but this is simplest } if ( buffswap != 0 ) // if implementing double buffering in master { msa = store[lsa+64]; // swapping route and pseudo-route -- this is route msb = store[lsb+64]; // this is pseudo-route store[lsa+64] = msb; store[lsb+64] = msa; // now swapped over } k = store[lsb+64]; if ( dvdv[i] < 0 ) { dvdv[i] = open(leofn("MT", lsa), O_RDWR + O_BINARY); if ( dvdv[i] < 0 ) { perror(buff); illegal("Cannot open mag tape file", lsa); } // fprintf(diag, "MT route %02o opened for reading on UNIX device %d\n", lsa, dvdv[i]); skipheaders(lsa, "reading", k); mtmode[i] = 1; // 1 indicates read on Leo III if ( dvdv[i] < 0 ) perror(buff); } c = read(dvdv[i], store+k, sizeof(int)); if ( c <= 0 ) // read the block size ... c = readendMT(i, k); else { c = store[k] & 0x7FF; // experiment using top half for previous block's size read(dvdv[i], store+k, c*sizeof(int)); // ... and then the data } mtblk[i] = c; // record size of block just read if ( verbosity >= 1 ) { fprintf(diag, "Block size = %d\n", c); // number of small compartments read fprintf(diag, "rd%02o %05X %05X %05X %05X %05X%05X %05X%05X %05X%05X", lsa, store[k], store[k+1], store[k+2], store[k+3], store[k+5], store[k+4], store[k+7], store[k+6], store[k+9], store[k+8]); } sex2oct(k, c); // convert sextets to octets if ( verbosity >= 1 ) { fprintf(diag, " << %s", tostr(store[k+5], store[k+4])); fprintf(diag, "%s", tostr(store[k+7], store[k+6])); fprintf(diag, "%s", tostr(store[k+9], store[k+8])); fprintf(diag, "%s\n", tostr(store[k+11], store[k+10])); // fprintf(diag, "Count word 48 = %06X %05X\n", store[k+48], store[k+49]); } msa = msb = lsa = lsb = 0; } else if ( msa == 8 ) // magnetic tape output via intercode 43 { if ( verbosity >= 1 ) { fprintf(diag, "%5d FILE output route %02o action %d after %d instructions\n", sc-1, lsa, msa, count); fprintf(diag, "Data address %d (pseudo %d)\n", store[lsa+64], store[lsb+64]); } if ( dvdv[i] < 0 ) { dvdv[i] = open(leofn("MT", lsa), O_CREAT + O_RDWR + O_BINARY, 0640); // removed O_TRUNC + if ( dvdv[i] < 0 ) { perror(buff); illegal("Cannot open mag tape file", lsa); } // fprintf(diag, "MT route %02o opened read-write on UNIX device %d\n", lsa, dvdv[i]); skipheaders(lsa, "writing", store[lsa+64]); // use the original annex as workspace mtmode[i] = 0; // 0 indicates write on Leo III mtblk[i] = 0; // 0 indicates start of tape mtcnt[i] = 7; // 8 indicates start of user data - may need to write header blocks if ( dvdv[i] < 0 ) perror(buff); } k = store[lsb+64]; // data to be written is in the section addresses by the pseudo-route if ( buffswap != 0 ) // if implementing double buffering in master { msa = store[lsa+64]; // swapping route and pseudo-route -- this is route msb = store[lsb+64]; // this is pseudo-route store[lsa+64] = msb; store[lsb+64] = msa; // now swapped over } if ( mtmode[i] != 0 && verbosity > 0 ) // i gives true file number fprintf(diag, "Warning: Writing on an input file - mode = %d\n", mtmode[i]); mtmode[i] = 0; // switch mode to write so as to get termination msd = 0; lsd = mtcnt[i]; // put in master's block count mtcnt[i] = ++lsd; storeDecimal(lsd, k+1); store[k+2] = 0; store[k+3] = 0; // is this a good idea? if ( verbosity >= 1 ) { fprintf(diag, "wr%02o [%d] %05X %05X %05X %05X %05X%05X %05X%05X %05X%05X", lsa, mtcnt[i], store[k], store[k+1], store[k+2], store[k+3], store[k+5], store[k+4], store[k+7], store[k+6], store[k+9], store[k+8]); fprintf(diag, " >> %s", tostr(store[k+5], store[k+4])); fprintf(diag, "%s", tostr(store[k+7], store[k+6])); fprintf(diag, "%s", tostr(store[k+9], store[k+8])); fprintf(diag, "%s\n", tostr(store[k+11], store[k+10])); // fprintf(diag, "Count word 48 = %06X %05X\n", store[k+48], store[k+49]); } j = 3; // skip first 4 words while ( ++j < 2000 && store[k+j] != 0x7E ) ; // stop at block end if ( j >= 2000 ) illegal("Oversize MT block", lsb); if ( verbosity >= 1 ) fprintf(diag, " Block end at %d %06X %06X\n", j, store[k+j], store[k+j+1]); c = store[--k]; // keep contents of location that we are going to borrow store[k] = j+2 // size of data block for later reading + (mtblk[i]<<11); // size of previous block in header word mtblk[i] = j+2; // size of data block for next time's write if ( write(dvdv[i], store+k, (j+3)*sizeof(int)) < 0 ) { sprintf(buff, "MT write on dv %d", dvdv[i]); perror(buff); illegal("MT write error", lsa); } store[k] = c; // repair the borrowed location dvstat[i] = 0; // now read and write address the same block } else if ( msa == 36 || msa == 35 ) // magnetic tape run back via intercode 46 { fprintf(diag, "%5d FILE RUN BACK route %02o index %d offset %d after %d instructions\n", sc-1, lsa, lsb, msb, count); j = 0; // diagnostic block counter k = store[64+lsa] & 0xFFFF; // use the annex as buffer space if ( mtmode[i] == 0 ) // output -- need to terminate tape { terminate(i); backstep(i, k); // skip back over the three terminator blocks backstep(i, k); backstep(i, k); } backread(i, k); msb ++; // point at top half of index word lsd = -1; // guarantee mismatch on first iteration while ( alignmark(i, k) != 0 || lsd != lsb ) { backstep(i, k); j++; backread(i, k); fetchAlpha(msb+k); // leaves binary value of index in lsd if ( verbosity >= 9 ) { fprintf(diag, " %06X %05X %s", store[k+5], store[k+4], tostr(store[k+5], store[k+4])); fprintf(diag, " %06X %05X %s", store[k+7], store[k+6], tostr(store[k+7], store[k+6])); fprintf(diag, " %06X %05X %s", store[k+9], store[k+8], tostr(store[k+9], store[k+8])); fprintf(diag, " lsd = %05X\n", lsd); } } fprintf(diag, "%5d FILE RUN BACK route %02o action %d, skipped %d blocks\n", sc-1, lsa, msa, j); } else if ( msa == 34 || msa == 33 || msa == 27 || msa == 28 ) // magnetic tape rewind via intercode 45 { char *op = "rewind"; // what is the difference between 33 and 34 if ( msa == 27 || msa == 28 ) op = "close"; k = dvdv[i]; // gives true file number fprintf(diag, "%5d FILE %s route %02o [mode = %d] after %d instructions\n", sc-1, op, lsa, mtmode[i], count); if ( k >= 0 ) // if device not already closed { if ( mtmode[i] == 0 ) // output -- need to terminate tape terminate(i); close(k); dvdv[i] = -1; // indicate no device allocated dvstat[i] = -1; // set device to manual } dvstat[i] = 0; // ready for next use of this device } else if ( msa == 39 || msa == 40 ) // STEP BACK via intercode 48 { fprintf(diag, "%5d STEP BACK route %02o action %d, block count %d after %d instructions\n", sc-1, lsa, msa, lsb, count); if ( mtmode[i] == 0 ) // output -- need to terminate tape terminate(i); k = store[64+lsa] & 0xFFFF; // use the annex as buffer space if ( lsb == 0 ) // ??? don't do the mode change option yet illegal("Do not understand STEP of 0", lsa); dvstat[i] = 0; // scrub out effect of RUN BACK -- is this right? while ( --lsb >= 0 ) backstep(i, k); } else if ( msa == 37 ) // RUN ON via intercode 47 { fprintf(diag, "%5d FILE RUN ON route %02o index %d offset %d after %d instructions\n", sc-1, lsa, lsb, msb, count); j = 0; // diagnostic block counter if ( mtmode[i] == 0 ) // output -- need to terminate tape illegal("RUN ON on output tape", lsa); k = store[64+lsa] & 0xFFFF; // use the annex as buffer space msb ++; // point at top half of index word lsd = -1; // guarantee mismatch on first iteration while ( alignmark(i, k) != 0 || lsd != lsb ) { j ++; c = read(dvdv[i], store+k, sizeof(int)); if ( c <= 0 ) // read the block size ... c = readendMT(i, k); else { c = store[k] & 0x7FF; // using top half for previous block's size read(dvdv[i], store+k, c*sizeof(int)); // ... and then the data } mtblk[i] = c; // record size of block just read // fprintf(diag, "Block size = %d\n", c); // number of small compartments read fetchAlpha(msb+k); // leaves binary value of index in lsd if ( verbosity >= 9 ) { fprintf(diag, " %06X %05X %s", store[k+5], store[k+4], tostr(store[k+5], store[k+4])); fprintf(diag, " %06X %05X %s", store[k+7], store[k+6], tostr(store[k+7], store[k+6])); fprintf(diag, " %06X %05X %s", store[k+9], store[k+8], tostr(store[k+9], store[k+8])); fprintf(diag, " lsd = %05X\n", lsd); } } dvstat[i] = 1; // indicates that next data to be read is already in the annex fprintf(diag, "%5d FILE RUN ON route %02o action %d, skipped %d blocks\n", sc-1, lsa, msa, j); } else bad = 1; } else if ( dvtype == 9 ) // Anelex printer on special Assembler (see 13.6) { if ( msa == 19 || msa == 11 // action 40 (open) on printer || msa == 24 ) // computer code open on type 14 in Intercode Translator { c = dvfn[lsa]; fprintf(diag, "Open printer %c%c on route %02o (Q6 = %d)\n", alpha[c>>8], alpha[c&0xFF], lsa, msa); if ( dvdv[i] < 0 ) // only open file first time dvdv[i] = open(leofn("LP", lsa), O_CREAT + O_TRUNC + O_WRONLY + O_BINARY, 0640); if ( dvdv[i] < 0 ) { perror(buff); illegal("Cannot open printer file", lsa); } } else if ( msa == 21 || msa == 29 // close on printer type 9 || msa == 34 ) // close on printer type 14 { msb = dvfn[i]; fprintf(diag, "Close printer %c%c on route %02o ==\n", alpha[msb>>8], alpha[msb&0xFF], lsa); } else if ( msa == 1 || msa == 9 // print a line on type 9 || msa == 4 ) // print a line on type 14 { if ( verbosity >= 1 ) { fprintf(diag, "%5d Output to printer route %02o action %d after %d instructions\n", sc-1, lsa, msa, count); fprintf(diag, "Data address %d (pseudo %d)\n", store[lsa+64], store[lsb+64]); } k = store[lsb+64]; i = 0; writelp(k, dvdv[lsa]); } else bad = 1; } else bad = 1; if ( bad != 0 ) { sprintf(buff+300, "unknown group 4 action %d device type %d (%d) on route %02o\n", msa, dvtype, dvty[lsa], lsa); notImplemented(buff+300); } } void master(int nnn) // implement master routine action ENTER MASTER ROUTINE // only implements those calls found in the Intercode Translator // much guesswork is involved. // entries 40-45 correspond with intercode actions of 40-45 // entry 1 is a kludge for PROCR 126, but it begins to look as though this should be 160 // Global variable count is the count of instructions executed so far, and is printed in the trace { int i, c, j, k; if ( nnn == 1 ) // open file or transfer - intercode group 4 { if ( verbosity >= 1 ) fprintf(diag, "%5d FILE OPERATION from line 12607 route %02o action %05X B = %06X %05X\n", sc-1, lsa, msa, msb, lsb); if ( msa == 15 && ptr == NULL ) // open paper tape reader { ptr = fopen(ptr_fn, "r"); if ( ptr == NULL ) { perror(ptr_fn); illegal("Cannot open paper tape reader file", lsa); } fprintf(diag, "%5d paper tape reader %c%c opened reading %s route %02o ==[%d] after %d instructions\n", sc-1, alpha[msb>>8], alpha[msb&0xFF], ptr_fn, lsa, msa, count); dvfn[lsa] = msb; // remember the filename for later - may not ever be used } else if ( msa == 5 ) // read paper tape { char *test = readptrline(ptr); if ( verbosity >= 1 ) { fprintf(diag, "Reading route %02o (pseudo = %d) into location %d\n", lsa, lsb, store[lsa+64]); fprintf(diag, "Data address %d (pseudo %d)\n", store[lsa+64], store[lsb+64]); fprintf(diag, "Data read was == %s (group 4 - %d instructions)\n", (char *)buff, count); } if ( test == NULL ) illegal("Read off end of paper tape", 0); i = -1; j = 0; while ( (c = buff[++i]) != 0 ) if ( (c = charval[c&255]) >= 0 ) buff[j++] = c; buff[j++] = BLOCKEND; // should this be line end or block end? buff[j] = 0; buff[j+1] = 0; // should these be zero? yes?? buff[j+2] = 0; buff[j+3] = 0; // to guarantee a full word if ( buffswap != 0 ) // if implementing double buffering in master { msa = store[lsa+64]; // swapping route and pseudo-route -- this is route msb = store[lsb+64]; // this is pseudo-route store[lsa+64] = msb; store[lsb+64] = msa; // now swapped over } i = store[lsb+64]; // address of transfer area k = 0; while ( k < j ) // 5 chars per long compartment { msa = (buff[k]<<12) | (buff[k+1]<<4) | (buff[k+2]>>4); lsa = ((buff[k+2]<<16) | (buff[k+3]<<8) | buff[k+4]) & 0xFFFFF; store[i++] = lsa; store[i++] = msa; k += 5; } // store[i+1] = 0x7E7E7; store[i] = 0xE7E7E; // unnecessary ?? -- or even unwise i = store[lsb+64]; // address of transfer area if ( verbosity >= 1 ) { fprintf(diag, "Stored %05X %05X at %d\n", store[i], store[i+1], i); fprintf(diag, "Big print %05X %05X - %05X %05X - %05X %05X - %05X %05X - %05X %05X - %05X %05X\n", store[i+1], store[i], store[i+3], store[i+2], store[i+5], store[i+4], store[i+7], store[i+6], store[i+9], store[i+8], store[i+11], store[i+12]); } } else if ( msa == 25 ) // close paper tape reader { fprintf(diag, "Closing paper tape reader\n"); // fclose(ptr); // don't actually close the file } else illegal("Bad call to Master Routine", msa); msa = msb = lsa = lsb = 0; } else if ( nnn == 40 ) // open file - Q6 of A is 0 for write and 1 for read { i = lsa; mtmode[i] = msa&0xF; dvfn[lsa] = msb; // remember the filename for later use on open fprintf(diag, "%5d FILE %c%c opened mode %d route %02o ==[%d] after %d instructions\n", sc-1, alpha[msb>>8], alpha[msb&0xFF], mtmode[i], lsa, msa, count); } else if ( nnn == 42 ) // input via intercode 42 { if ( verbosity >= 1 ) { fprintf(diag, "%5d FILE input route %02o action %d after %d instructions\n", sc-1, lsa, msa, count); fprintf(diag, "Data address %d (pseudo %d)\n", store[lsa+64], store[lsb+64]); } i = lsa; // gives true file number if ( mtmode[i] != 1 && verbosity > 0 ) fprintf(diag, "Warning: Reading on an output file - mode = %d\n", mtmode[i]); if ( dvstat[i] > 0 ) // MT read after RUN BACK or RUN ON { dvstat[i] = 0; backstep(i, 140); // data is actually in the annex, but this is simplest } if ( buffswap != 0 ) // if implementing double buffering in master { msa = store[lsa+64]; // swapping route and pseudo-route -- this is route msb = store[lsb+64]; // this is pseudo-route store[lsa+64] = msb; store[lsb+64] = msa; // now swapped over } k = store[lsb+64]; if ( dvdv[i] < 0 ) { dvdv[i] = open(leofn("MT", lsa), O_RDWR + O_BINARY); if ( dvdv[i] < 0 ) { perror(buff); illegal("Cannot open mag tape file", lsa); } skipheaders(lsa, "reading", k); mtmode[i] = 1; // 1 indicates read on Leo III if ( dvdv[i] < 0 ) perror(buff); } c = read(dvdv[i], store+k, sizeof(int)); if ( c <= 0 ) // read the block size ... readendMT(i, k); else { c = store[k] & 0x7FF; // experiment using top half for previous block's size read(dvdv[i], store+k, c*sizeof(int)); // ... and then the data } mtblk[i] = c; // record size of block just read if ( verbosity >= 1 ) { fprintf(diag, "Block size = %d\n", c); // number of small compartments read fprintf(diag, "rd%02o %05X %05X %05X %05X %05X%05X %05X%05X %05X%05X", lsa, store[k], store[k+1], store[k+2], store[k+3], store[k+5], store[k+4], store[k+7], store[k+6], store[k+9], store[k+8]); } sex2oct(k, c); // convert sextets to octets if ( verbosity >= 1 ) { fprintf(diag, " << %s", tostr(store[k+5], store[k+4])); fprintf(diag, "%s", tostr(store[k+7], store[k+6])); fprintf(diag, "%s", tostr(store[k+9], store[k+8])); fprintf(diag, "%s\n", tostr(store[k+11], store[k+10])); // fprintf(diag, "Count word 48 = %06X %05X\n", store[k+48], store[k+49]); } msa = msb = lsa = lsb = 0; } else if ( nnn == 43 ) // output via intercode 43 { if ( verbosity >= 1 ) { fprintf(diag, "%5d FILE output route %02o action %d after %d instructions\n", sc-1, lsa, msa, count); fprintf(diag, "Data address %d (pseudo %d)\n", store[lsa+64], store[lsb+64]); } i = lsa; // gives true file number if ( dvdv[i] < 0 ) // no device yet allocated { dvdv[i] = open(leofn("MT", lsa), O_CREAT + O_RDWR + O_BINARY, 0640); // removed O_TRUNC + if ( dvdv[i] < 0 ) { perror(buff); illegal("Cannot open mag tape file", lsa); } // fprintf(diag, "MT route %02o opened read-write on UNIX device %d\n", lsa, dvdv[i]); skipheaders(lsa, "writing", store[lsa+64]); // use the original annex as workspace mtmode[i] = 0; // 0 indicates write on Leo III mtblk[i] = 0; // 0 indicates start of tape mtcnt[i] = 7; // 8 indicates start of user data - may need to write header blocks if ( dvdv[i] < 0 ) perror(buff); } k = store[lsb+64]; if ( buffswap != 0 ) // if implementing double buffering in master { msa = store[lsa+64]; // swapping route and pseudo-route -- this is route msb = store[lsb+64]; // this is pseudo-route store[lsa+64] = msb; store[lsb+64] = msa; // now swapped over } if ( mtmode[i] != 0 && verbosity > 0 ) fprintf(diag, "Warning: Writing on an input file - mode = %d\n", mtmode[i]); mtmode[i] = 0; // switch mode to write so as to get termination msd = 0; lsd = mtcnt[i]; // put in master's block count mtcnt[i] = ++lsd; storeDecimal(lsd, k+1); store[k+2] = 0; store[k+3] = 0; // is this a good idea? if ( verbosity >= 1 ) { fprintf(diag, "wr%02o [%d] %05X %05X %05X %05X %05X%05X %05X%05X %05X%05X", lsa, mtcnt[i], store[k], store[k+1], store[k+2], store[k+3], store[k+5], store[k+4], store[k+7], store[k+6], store[k+9], store[k+8]); fprintf(diag, " >> %s", tostr(store[k+5], store[k+4])); fprintf(diag, "%s", tostr(store[k+7], store[k+6])); fprintf(diag, "%s", tostr(store[k+9], store[k+8])); fprintf(diag, "%s\n", tostr(store[k+11], store[k+10])); // fprintf(diag, "Count word 48 = %06X %05X\n", store[k+48], store[k+49]); } j = 3; // skip first 4 words while ( ++j < 2000 && store[k+j] != 0x7E ) ; // stop at block end if ( j >= 2000 ) illegal("Oversize MT block", lsb); if ( verbosity >= 1 ) fprintf(diag, " Block end at %d %06X %06X\n", j, store[k+j], store[k+j+1]); c = store[--k]; // keep contents of location that we are going to borrow store[k] = j+2 // size of data block for later reading + (mtblk[i]<<11); // size of previous block in header word mtblk[i] = j+2; // size of data block for next time's write if ( write(dvdv[i], store+k, (j+3)*sizeof(int)) < 0 ) { sprintf(buff, "MT write on dv %d", dvdv[i]); perror(buff); illegal("MT write error", lsa); } store[k] = c; // repair the borrowed location dvstat[i] = 0; // now read and write address the same block } else if ( (nnn&0xFB) == 41 ) // rewind via intercode 45 or close via intercode 41 { char *op = "rewind"; if ( nnn == 41 ) op = "close"; i = lsa; // gives true file number k = dvdv[i]; fprintf(diag, "%5d FILE %s route %02o [mode = %d] after %d instructions\n", sc-1, op, lsa, mtmode[i], count); if ( k >= 0 ) // if device not already closed { if ( mtmode[i] == 0 ) // output -- need to terminate tape terminate(i); close(k); dvdv[i] = -1; } dvstat[i] = 0; // ready for next use of this device } else if ( nnn == 46 ) // RUN BACK via intercode 46 { fprintf(diag, "%5d FILE RUN BACK route %02o index %d offset %d after %d instructions\n", sc-1, lsa, lsb, msb, count); i = lsa; // gives true file number j = 0; // diagnostic block counter if ( mtmode[i] == 0 ) // output -- need to terminate tape terminate(i); k = store[64+lsa] & 0xFFFF; // use the annex as buffer space backread(i, k); msb ++; // point at top half of index word lsd = -1; // guarantee mismatch on first iteration while ( alignmark(i, k) != 0 || lsd != lsb ) { backstep(i, k); j++; backread(i, k); fetchAlpha(msb+k); // leaves binary value of index in lsd if ( verbosity >= 9 ) { fprintf(diag, " %06X %05X %s", store[k+5], store[k+4], tostr(store[k+5], store[k+4])); fprintf(diag, " %06X %05X %s", store[k+7], store[k+6], tostr(store[k+7], store[k+6])); fprintf(diag, " %06X %05X %s", store[k+9], store[k+8], tostr(store[k+9], store[k+8])); fprintf(diag, " lsd = %05X\n", lsd); } } fprintf(diag, "%5d FILE RUN BACK route %02o action %d, skipped %d blocks\n", sc-1, lsa, msa, j); } else if ( nnn == 47 ) // RUN ON via intercode 47 { fprintf(diag, "%5d FILE RUN ON route %02o index %d offset %d after %d instructions\n", sc-1, lsa, lsb, msb, count); i = lsa; // gives true file number j = 0; // diagnostic block counter if ( mtmode[i] == 0 ) // output -- need to terminate tape illegal("RUN ON on output tape", lsa); k = store[64+lsa] & 0xFFFF; // use the annex as buffer space msb ++; // point at top half of index word lsd = -1; // guarantee mismatch on first iteration while ( alignmark(i, k) != 0 || lsd != lsb ) { j ++; c = read(dvdv[i], store+k, sizeof(int)); if ( c <= 0 ) // read the block size ... c = readendMT(i, k); else { c = store[k] & 0x7FF; // using top half for previous block's size read(dvdv[i], store+k, c*sizeof(int)); // ... and then the data } mtblk[i] = c; // record size of block just read // fprintf(diag, "Block size = %d\n", c); // number of small compartments read fetchAlpha(msb+k); // leaves binary value of index in lsd if ( verbosity >= 9 ) { fprintf(diag, " %06X %05X %s", store[k+5], store[k+4], tostr(store[k+5], store[k+4])); fprintf(diag, " %06X %05X %s", store[k+7], store[k+6], tostr(store[k+7], store[k+6])); fprintf(diag, " %06X %05X %s", store[k+9], store[k+8], tostr(store[k+9], store[k+8])); fprintf(diag, " lsd = %05X\n", lsd); } } dvstat[i] = 1; // indicates that next data to be read is already in the annex fprintf(diag, "%5d FILE RUN ON route %02o action %d, skipped %d blocks\n", sc-1, lsa, msa, j); } else if ( nnn == 48 ) // STEP BACK via intercode 48 { fprintf(diag, "%5d STEP BACK route %02o action %d, block count %d after %d instructions\n", sc-1, lsa, msa, lsb, count); i = lsa; // gives true file number if ( mtmode[i] == 0 ) // output -- need to terminate tape terminate(i); k = store[64+lsa] & 0xFFFF; // use the annex as buffer space if ( lsb == 0 ) // ??? don't do the mode change option yet illegal("Do not understand STEP of 0", lsa); dvstat[i] = 0; // scrub out effect of RUN BACK -- is this right? while ( --lsb >= 0 ) backstep(i, k); } else if ( nnn >= 100 && nnn < 116 ) // overlay, nnn = 101 + overlay number { i = nnn - 101; fprintf(diag, "\nEntering overlay %d, starts at %d - %ld short words - after %d instructions\n", i, oladdr[i], olsize[i]/sizeof(int), count); memcpy(store + oladdr[i], overlay[i], olsize[i]); // preserved overlay copied back to true location } else if ( nnn == 160 ) // this seems to be a general output (or I/O?) routine group4(); // eventually this should do the same as the real MR // suppress the old code, while we develop MR calls that more nearly match the real thing else if ( nnn == 9160 ) // this seems to be a general output (or I/O?) routine { if ( verbosity >= 1 ) // actually it seems to be that all entries to MR go this way fprintf(diag, "%5d FILE operation 160 %d action %d after %d instructions\n", sc-1, lsa, msa, count); // the route number is in Q1-5 of A, and there is a mystery in Q6-10 of A // mode info -- a query indicates confident prediction, but not actually seen in execution // 0x19 = close PTR // 0x1A = close CR ? // 0x04 = print on Anelex // 0x05 = read PTR // 0x06 = read CR ? // 0x0F = open PTR more info in B Q6-7 = file name (line 14870 - uncommented) // 0x10 = open CR ? // 0x18 = open printer, comment = "OPEN PRINTER OR MT A9" // 0x22 = close printer // sometimes Q1-5 of B hold address of transit area if ( lsa == 9 && msa == 0x18 ) // open printer { i = lsa; c = dvfn[lsa]; fprintf(diag, "Open printer %c%c on route %02o ==\n", alpha[c>>8], alpha[c&0xFF], lsa); if ( dvdv[i] < 0 ) // only open file first time dvdv[i] = open(leofn("LP", lsa), O_CREAT + O_TRUNC + O_WRONLY + O_BINARY, 0640); if ( dvdv[i] < 0 ) { perror(buff); illegal("Cannot open printer file", lsa); } } else if ( lsa == 9 && msa == 0x04 ) // print a line { if ( verbosity >= 1 ) { fprintf(diag, "%5d Output to printer route %02o action %d after %d instructions\n", sc-1, lsa, msa, count); fprintf(diag, "Data address %d (pseudo %d)\n", store[lsa+64], store[lsb+64]); } k = store[lsb+64]; i = 0; writelp(k, dvdv[lsa]); } else if ( lsa == 9 && msa == 0x22 ) // close printer fprintf(diag, "%5d Printer closed\n", sc-1); // leave the file allocated else notImplemented("FILE OPERATION 160"); } else if ( nnn == 154 ) // comment in log { k = lsa; i = -1; // last char put in buff while ( ++i < 12 ) // limited to 60 chars = 12 words { int msw = store[k+i+i+1]; int lsw = store[k+i+i]; strcpy(buff + i*5, tostr(msw, lsw)); if ( (lsw&0x3F3F) == 0x1E1E ) // two line end chars at end of word i = 8192+i; // sure to stop the loop } if ( i >= 8192 ) // two line ends found buff[(i&8191)*5-2] = 0; // remove the marker and also the two line feeds else buff[i*5] = 0; // terminate string which did not end with 2 line ends fprintf(diag, "%5d Comment in log: %s\n", sc-1, (char *)buff); printf("%5d LOG: %s\n", sc-1, (char *)buff); } else if ( nnn == 4 ) // implement geninely translated group 4 actions group4(); else if ( nnn == 5 ) // implement geninely translated overlay dooverlay(); else if ( nnn == 3 ) // call from genuine code -- probably 160 in due course { fprintf(diag, "%5d master call route %02o action %02X, lsb = %d after %d instructions\n", sc-1, lsa, msa, lsb, count); i = lsa; // gives true file number if ( msa == 0xB ) // open { int c1 = alpha[msb>>8]; int c2 = alpha[msb&0xFF]; fprintf(diag, "Open file %c%c for program %05X on route %02o\n", c1, c2, lsb, lsa); dvfn[lsa] = msb; // remember the filename for later - may not ever be used if ( i == 0 ) { msa = 0x18; master(160); } } else if ( msa == 0x1 ) // write ?? on printer ?? { fprintf(diag, "Guessing that action 1 is print\n"); msa = 4; master(160); } else if ( msa == 0x5 ) // read paper tape { if ( ptr == NULL ) ptr = fopen(ptr_fn, "r"); if ( ptr == NULL ) { perror(ptr_fn); illegal("Cannot open paper tape reader file", lsa); } fprintf(diag, "%5d paper tape reader %c%c opened on read reading %s route %02o ==[%d] after %d instructions\n", sc-1, alpha[msb>>8], alpha[msb&0xFF], ptr_fn, lsa, msa, count); master(1); // use our old routine } else if ( msa == 0x15 ) // close ?? { fprintf(diag, "Closing route %02o\n", lsa); i = lsa; // gives true file number close(dvdv[i]); dvdv[i] = -1; } else notImplemented("just yet"); fprintf(diag, "!!!!!\n"); } else { sprintf(buff+500, "ENTER MASTER ROUTINE %d", nnn); notImplemented(buff+500); } } void importMT(char *fn) // Import a tape from architecture-neutral format { int f1; // input file int f2; // output file int bufflen = 32768; int intsz = sizeof(int); int bc = 0; // block count int i, j, k, n; int prev = 0; unsigned char *bbuff; // byte buffer int *ibuff; // integer buffer i = strlen(fn); while ( --i >= 0 && fn[i] != '.' ) ; // find the last dot if ( i < 0 ) fprintf(stderr, "bad tape name (no dot): %s\n", fn); else { fn[i] = 0; printf("Creating Leo III tape %s.leo from %s.%s\n", fn, fn, fn+i+1); fn[i] = '.'; f1 = open(fn, O_RDONLY + O_BINARY); if ( f1 < 0 ) { perror(fn); return; } strcpy(fn+i, ".leo"); f2 = open(fn, O_WRONLY + O_BINARY + O_CREAT + O_TRUNC, 0640); if ( f2 < 0 ) { close(f1); perror(fn); return; } ibuff = (int *)malloc(maxblox * intsz); bbuff = (unsigned char *)malloc(maxblox * 4); if ( ibuff == NULL || bbuff == NULL ) { fprintf(stderr, "Cannot allocate buffers: %s\n", fn); return; } while ( (n = read(f1, bbuff, 4)) == 4 ) // header word read OK { int sz = (bbuff[0]<<24) + (bbuff[1]<<16) + (bbuff[2]<<8) + bbuff[3]; bc ++; if ( (n = read(f1, bbuff, sz)) != sz ) // bad format tape { printf("Short block read %d != %d at block %d\n", n, sz, bc); if ( n < 0 ) perror(fn); exit(1); } // printf("Block %d size %d\n", bc, sz); j = 1; for ( i = 0; i>4); int v0 = (bbuff[i+2]<<16) + (bbuff[i+3]<<8) + bbuff[i+4]; ibuff[j++] = v0 & 0xFFFFF; ibuff[j++] = v1 & 0xFFFFF; } ibuff[0] = (prev<<11) + j - 1; // block size in first integer + size of previous prev = j - 1; write(f2, ibuff, j*intsz); // printf("Written block %d Leo size %d, byte size %d %02X %02X %02X %02X\n", bc, j-1, sz, bbuff[0], bbuff[1], bbuff[2], bbuff[3]); } close(f1); close(f2); free(ibuff); free(bbuff); if ( n != 0 ) printf("Suspect format error at end of tape: %d bytes read\n", n); } } void mountMT(int route, char *fn) // mount file fn on route // note: route is read in as decimal { int dv = open(fn, O_RDWR + O_BINARY, 0640); int d8 = route/10; int d0 = route%10; route = d8*8 + d0; if ( dv < 0 ) perror(fn); else { if ( dvdv[route] >= 0 ) { close(dvdv[route]); printf("Unloaded route %02o\n", route); } dvdv[route] = dv; printf("Mounting %s on route %d/%d\n", fn, route>>3, route&7); mtblk[route] = 0; // size of block behind tape head -- start of tape dvstat[route] = 0; // set up as normal } } void lpop(int i, int func) // output to line printer // currently func is ignored { int kmsa, klsa; if ( dvdv[i] < 0 ) // printer not open { sprintf(buff, "Printer not available: LP-R%02o.txt", i); // just in case it fails if ( lpsock >= 0 ) { dvdv[i] = lpsock; // this needs more sophistication fprintf(diag, "%5d printer open on route %02o: %s:8889\n", sc-1, i, netprint); } else { dvdv[i] = open(buff+23, O_WRONLY + O_TRUNC + O_CREAT, 0640); fprintf(diag, "%5d printer open on route %02o: %s\n", sc-1, i, (char *)buff+23); } mtcnt[i] = 0; // probably not necessary if ( dvdv[i] < 0 ) { perror(buff+24); illegal(buff, i); } } writelp(store[i+64] & 077777, dvdv[i]); // general purpose printer output routine #ifndef WIN32 if ( lpsock >= 0 ) fsync(lpsock); // stop CPU massively overtaking the printer window -- not convinced that it works #endif } void mtio(int i, int func) // master level I/O on mag tape route i // func is 4*d + m, i.e. f&7 { int j, k, c; while ( dvdv[i] < 0 ) // device not open -- keep trying { sprintf(buff, "Mag tape not available: MT-R%02o.leo", i); // just in case it fails dvdv[i] = open(buff+24, O_RDWR + O_BINARY, 0640); fprintf(diag, "%5d re-open on route %02o: %s\n", sc-1, i, (char *)buff+24); mtcnt[i] = 0; // master mode, tape starts in front of headers j = ((i&070)<<5) | (i & 7) | 0x4040; // route no in chars for diagnostics if ( (i & 070) == 0 ) // but zero is funny on Leo III j -= 0x1000; if ( (i & 07) == 0 ) j -= 0x10; if ( dvdv[i] < 0 ) { perror(buff+24); // illegal(buff, i); printf("Need tape mounting on route %02o\n", i); buff[3] = 'M'; // M for mag tape -- shows need for tape mount buff[4] = i; readkbd(3); // give chance for a mount command opscometc(); // keep going until abandoned by x or successful mount } } if ( func == 0 ) // MT OUTPUT via 19/0/0 { k = store[64+i] & 0xFFFE; // address of the annex mtcnt[i] ++; if ( verbosity >= 1 ) { fprintf(diag, "%5d MT OUTPUT route %02o annex %d after %d instructions\n", sc-1, i, k, count); fprintf(diag, "wr%02o [%d] %05X %05X %05X %05X %05X%05X %05X%05X %05X%05X", i, mtcnt[i], store[k], store[k+1], store[k+2], store[k+3], store[k+5], store[k+4], store[k+7], store[k+6], store[k+9], store[k+8]); fprintf(diag, " >> %s", tostr(store[k+5], store[k+4])); fprintf(diag, "%s", tostr(store[k+7], store[k+6])); fprintf(diag, "%s", tostr(store[k+9], store[k+8])); fprintf(diag, "%s\n", tostr(store[k+11], store[k+10])); // fprintf(diag, "Count word 48 = %06X %05X\n", store[k+48], store[k+49]); } j = 3; // skip first 4 words while ( ++j < 2000 && store[k+j] != 0x7E ) ; // stop at block end if ( j >= 2000 ) illegal("Oversize MT block", lsb); if ( verbosity >= 1 ) fprintf(diag, " Block end at %d %06X %06X\n", j, store[k+j], store[k+j+1]); c = store[--k]; // keep contents of location that we are going to borrow store[k] = j+2 // size of data block for later reading + (mtblk[i]<<11); // size of previous block in header word mtblk[i] = j+2; // size of data block for next time's write if ( write(dvdv[i], store+k, (j+3)*sizeof(int)) < 0 ) { sprintf(buff, "MT write on dv %d", dvdv[i]); perror(buff); illegal("MT write error", lsa); } store[k] = c; // repair the borrowed location dvstat[i] = 0; // now read and write address the same block if ( store[k+2] == 0x07A556 ) // end of tape block { j = lseek(dvdv[i], 0, SEEK_CUR); ftruncate(dvdv[i], j); // chop off any data beyond the terminator } } else if ( (func&255) == 1 ) // INPUT via 19/0/1 or STEP ON via 19/1/3 { k = store[64+i] & 0xFFFE; // address of the annex j = k; // used in STEP ON if ( k == 0 ) // start key used lastoverlay = 12418; // we are doing boot load - this makes diagnostic addresses fit with listing if ( func >= 256 ) // STEP ON k = 8192*4; c = read(dvdv[i], store+k, sizeof(int)); if ( c <= 0 ) // read the block size ... c = readendMT(i, k); // read off end of tape - supply finish block else { c = store[k] & 0x7FF; // using top half for previous block's size read(dvdv[i], store+k, c*sizeof(int)); // ... and then the data } if ( verbosity >= 1 ) fprintf(diag, "%5d MT INPUT/STEP-ON route %02o annex %d after %d instructions - block size %d\n", sc-1, i, k, count, c); mtblk[i] = c; // record size of block just read if ( c > maxblox ) // unusually large block illegal("Bad tape block size", c); mtcnt[i] ++; sex2oct(k, c); // convert sextets to octets if ( func >= 256 ) // STEP ON -- copy the one long word { store[j] = store[8192*4]; store[j+1] = store[8192*4+1]; } if ( verbosity >= 8 ) fprintf(diag, " %d words %05X %05X %05X %05X %05X %05X -- %05X %05X\n", c, store[k], store[k+1], store[k+2], store[k+3], store[k+4], store[k+5], store[k+14], store[k+15]); } else if ( func == 2 ) // RUN BACK via 19/0/2 { fprintf(diag, "%5d MT RUN BACK route %02o after %d instructions\n", sc-1, i, count); j = 1; // diagnostic block counter k = 8192*4; // use top of store as buffer -- not space visible to Leo III backread(i, k); while ( alignmark(i, k) != 0 ) { backstep(i, k); j++; backread(i, k); } backstep(i, k); // step back to before the block containing the alignment mark if ( verbosity >= 1 ) fprintf(diag, "%5d MT RUN BACK route %02o action %d, skipped %d blocks (last one size = %d block %05X %05X)\n", sc-1, i, func, j, mtblk[i], store[k+1], store[k]); } else if ( func == 3 ) // RUN ON via 19/0/3 { if ( verbosity >= 1 ) fprintf(diag, "%5d MT RUN ON route %02o after %d instructions\n", sc-1, i, count); j = 0; // diagnostic block counter k = 8192*4; // use top of store as buffer -- not space visible to Leo III c = 1; // indicates alignment mark not yet found while ( c > 0 ) // alignment mark not found and not read error { j ++; c = read(dvdv[i], store+k, sizeof(int)); mtcnt[i] ++; if ( c > 0 ) // if read OK { c = store[k] & 0x7FF; // get size of current data block read(dvdv[i], store+k, c*sizeof(int)); // ... and then the data mtblk[i] = c; // record size of block just read c = alignmark(i, k); // zero result if block contains alignment mark } else c --; // -1 for end of tape, < -1 for error } if ( c < 0 ) // read off end of tape or error if ( c == -1 ) // read off end of tape - supply finish block mtblk[i] = readendMT(i, k); // record size of block just read else // transfer error { perror("action 19/0/3"); illegal("Tape error", i); } if ( verbosity >= 1 ) fprintf(diag, "%5d MT RUN ON route %02o action %d, skipped %d blocks (last one size = %d block %05X %05X)\n", sc-1, i, func, j, mtblk[i], store[k+1], store[k]); } else if ( func == 4 ) // STEP BACK via 19/1/0 { fprintf(diag, "%5d MT STEP BACK route %02o after %d instructions\n", sc-1, i, count); k = store[64+i] & 0xFFFE; // use the annex as buffer space -- this may not be OK here k = 8192*4; // use top of store as buffer -- not space visible to Leo III backstep(i, k); } else if ( func == 5 ) // REWIND via 19/1/1 { fprintf(diag, "%5d MT REWIND route %02o after %d instructions\n", sc-1, i, count); lseek(dvdv[i], 0, SEEK_SET); // position at start of tape } else if ( func == 6 ) // UNLOAD via 19/1/2 { struct stat filegen; j = -1; // just in case of no device if ( dvdv[i] >= 0 ) { fstat(dvdv[i], &filegen); j = lseek(dvdv[i], 0, SEEK_CUR); close(dvdv[i]); dvdv[i] = -1; } else filegen.st_size = -1; // unlikely to be obeyed - we hope fprintf(diag, "%5d route %02o unloaded after %d instructions\n", sc-1, i, count); printf("%5d Unloaded MT on route %02o [size = %d]\n", sc-1, i, (int)filegen.st_size); dvstat[i] = -1; // mark as unallocated } else { sprintf(buff+900, "Action 19/%d/%d", func>>2, func&3); notImplemented(buff+900); } } void typeout(int r, int anx) // output to the typewriter on route r via a 19/0/0 instruction // annex address is anx -- normally 154 // only 5 chars are output at a time by 19/0/0 so output is assembled in twbuff { static char twbuff[2008]; static int buffp = 0; int ch2, i, msa, lsa; char ch; char *twfmt = blkfmt; int lastnl = -1; // position of last newline in output buffer if ( buffp >= 500 ) illegal("Overflow T/W buffer", buffp); lsa = store[anx++]; msa = store[anx]; if ( msa == 0x005E5 && lsa == 0x97272 ) // typing out \nISS i.e. start of master routine lastoverlay = 160 - 18; // align diagnostic output with listing of master routine indflag = -1; // don't invite input until after typing is finished ch2 = lsa & 0xFFFF; // last 2 octets to test for end if ( verbosity > 0 ) fprintf(diag, "T/W %05X %05X", msa, lsa); for ( i = 0; i<5; i++ ) { twbuff[buffp + i] = alpha[(msa>>12)&0x3F]; msa = ((msa<<8) + (lsa>>12)) & 0xFFFFF; lsa = (lsa<<8) & 0xFFFFF; } twbuff[buffp + 5] = 0; if ( verbosity > 0 ) fprintf(diag, " %s\n", twbuff + buffp); buffp += 5; if ( ch2 == 0x5E5E ) // end of message { twbuff[--buffp] = 0; if ( buffp >= 6 ) indflag = 15; // ask for user input after next 15 INTERROGATE INDICATORS buffp = 0; if ( twbuff[0] == '\\' ) // output in red { buffp++; // skip the back slash twfmt = redfmt; // ANSI escape sequence to show O/P in red } i = 0; // first deal with tabs while ( (ch = twbuff[buffp++]) != 0 ) if ( ch != ';' && ch != '\n' ) buff[i++] = ch; // not number-end or newline else { while ( (i+1)%TWTAB != 0 ) buff[i++] = ' '; buff[i++] = ch; // put in ; to mark tab position } buff[i] = 0; buffp = 0; i = 0; // second deal with newlines while ( (ch = buff[i++]) != 0 ) if ( ch == ';' ) // tab twbuff[buffp++] = ' '; // gap between columns else if ( ch != '\n' ) // not newline twbuff[buffp++] = ch; else // endline |ISSUE 0 GO EVEN FASTER MASTER ROUTINE 09001 00401| sets width { if ( rhpad > 0 ) { memset(twbuff+buffp, ' ', 60); if ( buffp < lastnl+rhpad ) // just in case there is a long line output buffp = lastnl+rhpad; } strcpy(twbuff+buffp, cntfmt); lastnl = buffp; buffp += strlen(cntfmt); } if ( lastnl >= 0 ) // guard against output without a newline twbuff[lastnl] = 0; // chop off last newline printf(twfmt, sc - 1, twbuff); if ( echofmt != NULL ) // echoing typed input - probably redirected stdout fprintf(stderr, "%5d T/W: %s\n", sc - 1, twbuff); // so put a copy on stderr fprintf(diag, "%5d T/W route %02o: %s\n", sc - 1, r, twbuff); buffp = 0; } fprintf(diag, "\n"); } void readregval(char *s) // reads an initial register value into D { int w; if ( isdigit(*s) ) // ordinary number { w = atoi(s); lsd = w & 0xFFFFF; msd = w >> 20; } else if ( *s == 'A' ) // alpha { lsd = 0; msd = 0; while ( (w = *++s) != 0 ) { msd = (msd<<8) + (lsd>>12); lsd = ((lsd<<8) + charval[w&255]) & 0xFFFFF; } } else if ( *s == 'D' ) // hexadecimal using either Leo chars or ASCII { lsd = 0; msd = 0; while ( (w = *++s) != 0 ) { msd = (msd<<4) + (lsd>>16); lsd = ((lsd<<4) + hexval[w&255]) & 0xFFFFF; } } } int headmark1[] = { 0x000A, 0x41, 0, 0, 0, 0x00001, 0x05000, 0x34445, 0x41424, 0x7E, 0x7E000 }; int headmark2[] = { 0x500A, 0x42, 0, 0, 0, 0x00002, 0x05000, 0x34445, 0x41424, 0x7E, 0x7E000 }; int headmark3[] = { 0x500A, 0x43, 0, 0, 0, 0x00003, 0x05000, 0x34445, 0x41424, 0x7E, 0x7E000 }; int headmark4[] = { 0x500A, 0x44, 0, 0, 0, 0x00004, 0x05000, 0xF6F6F, 0x6F6F6, 0x7E, 0x7E000 }; int headmark5[] = { 0x500A, 0x45, 0, 0, 0, 0x00005, 0x05000, 0xF6F6F, 0x6F6F6, 0x7E, 0x7E000 }; int headmark6[] = { 0x500A, 0x46, 0, 0, 0, 0x00006, 0x05000, 0x34445, 0x41424, 0x7E, 0x7E000 }; int headmark7[] = { 0x5014, 0x47, 0, 0, 0, 0x00007, 0x1E000, 0x5142, 0, 0x41, 0, 0x41, 0, 0x90312, 0, // 9 March 2012 0x03030, 0x30483, 0, 0, 0x7E, 0x7E000 }; void blankMT(char *serial, char *fn) // make a new blank tape with serial no as fisr 5 chars of serial // and filename fn { int dv = open(fn, O_CREAT + O_TRUNC + O_RDWR + O_BINARY, 0640); int w0 = 0; int w1 = 0; int n; if ( dv < 0 ) // output tape perror(fn); for ( n = 0; n<5; n++ ) // make serial number in Leo III chars { if ( serial[n] == '0' ) w0 = (w0<<8) | 0x30; else w0 = (w0<<8) | (serial[n]-'0') | 0x40; w1 = (w1<<8) | (w0>>20); w0 &= 0xFFFFF; } headmark1[7] = w0; headmark1[8] = w1; headmark2[7] = w0; headmark2[8] = w1; headmark3[7] = w0; headmark3[8] = w1; headmark6[7] = w0; headmark6[8] = w1; write(dv, headmark1, sizeof(headmark1)); write(dv, headmark2, sizeof(headmark2)); write(dv, headmark3, sizeof(headmark3)); write(dv, headmark4, sizeof(headmark4)); write(dv, headmark5, sizeof(headmark5)); write(dv, headmark6, sizeof(headmark6)); serial[5] = 0; printf("Creating blank tape %s %s\n", serial, fn); write(dv, endmark, sizeof(endmark)); write(dv, endmark2, sizeof(endmark2)); write(dv, endmark3, sizeof(endmark3)); close(dv); } void catchint(int sig) { indflag = 0; // force ind reg to read signal(SIGINT, SIG_DFL); // allow 2nd control-C to terminate normally done19 = 1; // make sure that we are in master mode } void readkbd(int mode) // read keyboard input and echo if necessary { int i; showIndicators(mode); // elicit a console response if opsock >= 0 if ( opsock >= 0 ) // console window, - need to read socket #ifndef WIN32 read(opsock, buff, 80); // fixed read of 80 bytes #else recv(opsock, buff, 80, 0);// MS equivalent to socket read #endif else if ( fgets(buff, 999, stdin) == NULL ) // need to read stdin strcpy(buff, "x\n"); // will terminate run if it runs out of stdin i = strlen(buff); // remove trailing CRLF and/or white-space while ( --i >= 0 && buff[i] <= ' ' ) ; buff[i+1] = 0; if ( echofmt != NULL ) printf(echofmt, (char *)buff); } char *getfilename(char *fn) // gets the filename starting at fn, and terminating on 3 spaces or end string // i.e. strips comment from the end of a file name { int i = -1; static char res[200]; while ( fn[++i] != 0 && memcmp(fn+i, " ", 3) != 0 ) ; if ( fn[i] == 0 ) // no comment to be ignored if ( fn[0] == ' ' ) // skip initial space return fn + 1; else return fn; if ( i >= 200 ) illegal("Ridiculous file name", i); fn[i] = 0; strcpy(res, fn); fn[i] = ' '; if ( res[0] == ' ' ) // skip initial space return res + 1; return res; } void opscometc() // analyse user instructions in buff, such as set indicator, or mount tapes, etc // documented at the head of this source text { static int addr = 0; // so we can do repeated prints at the same address static int format = 0; // 0 for hex output, NZ for computer code int i = strlen(buff); if ( buff[0] < ' ' ) // just CR, do nothing return; while ( buff[--i] < ' ' ) ; buff[i+1] = 0; // chop off trailing CR etc if ( buff[0] == 'x' ) // cancel execution { i = atoi(buff+1); if ( i <= 0 ) illegal("Operator abort", 0); else abandon = count + i; } else if ( buff[0] == 'v' ) // set verbosity if ( buff[1] == 'p' ) // verbose prompt for indicator setting indregprompt = "%5d Set indicator register (currently %5X, mask %5X) === %d %d\n"; else verbosity = atoi(buff+1); else if ( buff[0] == 'n' ) // make new blank mag tape { if ( buff[1] == ' ' && buff[7] == ' ' ) // format OK blankMT(buff+2, buff+8); else printf("Should have been like this: n 12345 filename\n"); } else if ( buff[0] == 'm' ) // mount mag tape { char *fn = getfilename(buff+4); if ( buff[3] == 'c' ) // tape needs converting from architecture neutral format importMT(fn); // note: extension of fn is changed to .leo mountMT(atoi(buff+1), fn); } else if ( buff[0] == 'k' ) // konvert tape from keeptape format (architecture neutral) importMT(getfilename(buff+1)); else if ( buff[0] == 'p' ) // new paper tape to read { if ( ptr != NULL ) { printf("Disconnecting PTR file %s\n", ptr_fn); if ( ptr0 != NULL ) { fclose(ptr0); printf("Disconnecting included PTR file\n"); ptr0 = NULL; // remove any include file } fclose(ptr); } i = 1; if ( buff[i] == ' ' ) i ++; // ignore one space after command letter ptr_fn = strdup(getfilename(buff+i)); fprintf(diag, "Paper tape will now be read from %s after %d instructions\n", ptr_fn, count); printf("Paper tape will now be read from %s\n", ptr_fn); ptr = fopen(ptr_fn, "r"); if ( ptr == NULL ) perror(ptr_fn); else if ( ptroute >= 0 ) dvstat[ptroute] = 0; // set PTR ready } else if ( buff[0] == 'f' ) // format for print area of store format = atoi(buff+1); else if ( memcmp(buff, "date", 4) == 0 ) // request for date input { indreg = datereg; printf(" set to %03X\n", indreg); } else if ( buff[0] == 'd' ) // print area of store { if ( buff[1] == 'd' ) // dd moves on 10 words addr += 10; else if ( buff[1] != 0 ) // new monitor address specified addr = atoi(buff+1); // start address if ( format == 0 ) fprintf(diag, "%5d: %06X %06X %06X %06X %06X %06X %06X %06X %06X %06X\n", addr, store[addr], store[addr+1], store[addr+2], store[addr+3], store[addr+4], store[addr+5], store[addr+6], store[addr+7], store[addr+8], store[addr+9]); else if ( format == 1 ) { fprintf(diag, "%5d:", addr); for ( i = 0; i<10; i++ ) { int w = store[addr+i]; fprintf(diag, " %2d/%d/%d %d", w>>16, (w>>15)&1, (w>>13)&3, w&0x1FFF); } fprintf(diag, "\n"); } else { for ( i = 0; i<10; i++ ) { int w = store[addr+i]; fprintf(diag, "%5d: %06X %2d/%d/%d %4d %s\n", addr+i, w, w>>16, (w>>15)&1, (w>>13)&3, w&0x1FFF, ccstr[w>>13]); } fprintf(diag, "\n"); } } else if ( buff[0] == 'i' ) // show interrupt info { if ( ioauto == 0 ) printf("Interrupts were not enabled\n"); fprintf(diag, "Request = %d, count = %d, indicator = %05X\n", intreq, count, indreg); i = lastint & 32767; fprintf(diag, "Last interrupt was from %5d (%d,%d)\n", i, i&8191, i-lastoverlay); fprintf(diag, " Count = %d\n", count); for ( i = 0; i<64; i++ ) if ( dveng[i] > 0 ) fprintf(diag, "%02o: type %d, eng %d, stat %d\n", i, dvty[i], dveng[i], dvstat[i]); } else if ( buff[0] == 'c' ) // put comment in diagnostic output { fprintf(diag, "%5d: %s\n", sc, (char *)buff+1); if ( echofmt != NULL ) // if echoing input put this comment also on stdout printf("%5d: %s\n", sc, (char *)buff+1); } else if ( buff[0] == 'z' ) // set ind reg flag if ( buff[1] == 'z' ) indflag = 99999; else indflag = atoi(buff+1); else if ( buff[0] != 0 ) // ignore a blank line { if ( buff[1] == '/' ) // operator jargon for ind reg G/r/F { lsd = (atoi(buff)<<8) | (atoi(buff+2)<<4); if ( buff[3] == '/' ) // single digit for r lsd |= atoi(buff+4); else lsd |= atoi(buff+5); } else readregval(buff); // decode according to Intercode constants indreg &= 060000; // retain bits 14 and 15 indreg |= lsd & 017777; // and put in the new settings indflag = 900000000; // wait a long time } signal(SIGINT, catchint); // allow another control-C to come here again } void newsc(int dest) // set sequence counter to dest after a check for stupid values // not currently used { if ( dest >= 32768 ) illegal("Jumps out of memory", dest); sc = dest; } void interrupt(int loc) // generates an interrupt to enter master at loc+1 after planting address at loc // external interrupt has loc = 8, loc out loc = 16 and overflow loc = 24 { if ( (store[loc+1]&0x1FE000) != 0x1A2000 ) illegal("Lock out (or possibly overflow)", loc); tagreg = 14; indreg &= 0x1EFFF; // forbid further interrupts store[loc] = (tagreg << 16) + sc; // return address lastint = sc; if ( verbosity >= 1 ) fprintf(diag, "%5d Interrupt after %d instructions (indicator = %05X)\n", sc, count, indreg); sc = loc + 1; // enter master } void interpret(int addr) { int a, d, m, mval, smoval, f, pp; // pp is 2p as in the manual, nnn is N as in the manual (now declared global) int w, i; // working variables int kverbosity = -1; // keeps verbosity on entering key diag suppression routine count = 0; // number of instructions obeyed sc = addr; // sequence counter smoval = 0; // supplimentary modification to apply to next instruction if ( verbosity >= 6 ) { fprintf(diag, " _\n"); // put a bar over the N fprintf(diag, " SC N A B C/SMO M\n"); } while ( 1 == 1 ) { if ( ++count == abandon ) // total number of instructions obeyed illegal("execution abandonned", sc); if ( sc == faband ) // turn on idagnostics for 1000 then stop { abandon = count + 1000; verbosity = 9; fprintf(diag, "%5d Diagnostic encountered\n", sc); } if ( count == intreq ) // time to think about an interrupt if ( (store[9]&0x1FE000) != 0x1A2000 ) intreq = -1; // skip this if there is not yet an interrupt service routine else if ( (indreg&0x1000) == 0 // interrupts not allowed || smoval != 0 ) // or just obeyed a "SMO" instruction intreq ++; // try again next time else { intreq = -1; // clear request interrupt(8); // external interruption } if ( (count&1023) == 0 && mstime != 0 ) // 10 millisecs approx & clock implemented { store[157] += 0x10; // add 1 to the 1/100 sec field if ( (store[157]&0xF0) == 0xA0 ) { store[157] += 0x100 - 0xA0; if ( store[156] == 0 ) store[156] = nowleo; if ( (store[157]&0xF00) == 0xA00 ) { store[157] += 0x1000 - 0xA00; if ( ioauto > 1 // we are definitely processing interrupts && (store[157]&0x1000) != 0 ) // value = 1, 3, 5, 7 or 9 intreq = count + 2; else if ( (store[157]&0xF000) == 0xA000 ) { store[157] &= 0xFFF; store[156] += 1; // 1/10 min, need to tick Saxby clock if ( (++store[156]&0xF) == 10 ) // need to carry { store[156] += 6; // 1 min, need to tick Saxby clock if ( (store[156]&0xF0) == 0xA0 ) // 10 min carry if ( (store[156] += 0x60) & 0xF00 == 0x600 ) // 1 hour, need to tick Saxby clock if ( (store[156] += 0xA00) & 0xF000 == 0xA00 ) // 10 hour, need to tick Saxby clock store[156] += 0x6000; // forget about midnight for now } } } } } // if ( sc < 1000 // find out what goes on when we obey the master // && count >= 1000000 ) // and we are not doing a start key -- something of a kludge // lastoverlay = 160 - 18; // we are now in the master where location 160 looks like 18 in the listing if ( count == turnOn ) { fprintf(diag, " _\n"); // put a bar over the N fprintf(diag, " SC N A B C/SMO M\n"); if ( kverbosity >= 0 ) // we are inside the suppression routine kverbosity = 9; // arrange to turn on when we exit else verbosity = 9; } if ( monloc >= 0 && memcmp(store+monloc, monval, monnum*sizeof(int)) != 0 ) // something has changed { w = store[monloc]; if ( monnum < 2 ) fprintf(diag, "%5d Location %d changed from %d to %d %06X %2d/%d/%d %d after %d instructions\n", sc-1, monloc, monval[0], w, w, w>>16, (w>>15)&1, (w>>13)&3, w&0x1FFF, count); else // print 10 locations for the hell of it -- or even 20 { fprintf(diag, " Location %5d changed from %06X %06X %06X %06X %06X %06X %06X %06X %06X %06X\n", monloc, monval[0], monval[1], monval[2], monval[3], monval[4], monval[5], monval[6], monval[7], monval[8], monval[9]); fprintf(diag, "%5d = SC to %06X %06X %06X %06X %06X %06X %06X %06X %06X %06X\n", sc-1, store[monloc], store[monloc+1], store[monloc+2], store[monloc+3], store[monloc+4], store[monloc+5], store[monloc+6], store[monloc+7], store[monloc+8], store[monloc+9]); if ( monnum >= 11 ) { fprintf(diag, " Location %5d changed from %06X %06X %06X %06X %06X %06X %06X %06X %06X %06X\n", monloc+10, monval[10], monval[11], monval[12], monval[13], monval[14], monval[15], monval[16], monval[17], monval[18], monval[19]); fprintf(diag, " to %06X %06X %06X %06X %06X %06X %06X %06X %06X %06X\n", store[monloc+10], store[monloc+11], store[monloc+12], store[monloc+13], store[monloc+14], store[monloc+15], store[monloc+16], store[monloc+17], store[monloc+18], store[monloc+19]); } } memcpy(monval, store+monloc, 20*sizeof(int)); } instr = store[sc]; f = instr>>13; // function -- called OI in the manual a = f>>3; // action d = (f&4) >> 2; // discriminant m = f&3; // modifier ----- initially set to the modifier number, but then to modifier value when appropriate ... mval = store[groupref + m + m]; // .. by putting this value into m nnn = (instr & 017777) + (sc & 060000) + smoval; if ( verbosity >= 6 ) // only show blow-by-blow for verbosity >= 6 { if ( m == 0 ) strcpy(buff, " "); else sprintf(buff, " %5d", store[groupref + m + m] & 0x7FFF); // LS 15 bits of modifier if ( smoval == 0 ) // only show SMO if NZ, otherwise C-reg fprintf(diag, "%5d ---> %2d/%d/%d %5d || %06X %05X %06X %05X %05X%s | %s\n", sc, a, d, m, nnn, msa, lsa, msb, lsb, creg, (char *)buff, ccstr[f]); else if ( smoval < 0 ) // indicates suppression of division bits fprintf(diag, "%5d ---> %2d/%d/%d %5d || %06X %05X %06X %05X %5d*%s | %s\n", sc, a, d, m, nnn, msa, lsa, msb, lsb, smoval+(sc & 060000), (char *)buff, ccstr[f]); else fprintf(diag, "%5d ---> %2d/%d/%d %5d || %06X %05X %06X %05X %5d %s | %s\n", sc, a, d, m, nnn, msa, lsa, msb, lsb, smoval, (char *)buff, ccstr[f]); } smoval = 0; // clear down here so that 24 actions can set it as needed if ( sc == 2148 ) // exit to freshly loaded programme ??? { fprintf(diag, "Entering prog at %d (instr = %d/%d/%d %d)", // something of a kludge ... w = store[nnn], a, d, m, nnn); // .. to enable identification of source code on allocated programmes fprintf(diag, " Actions: %d %d %d %d %d %d %d %d Count %d\n", // listing first few actions to identify overlay - maybe store[w]>>16, store[w+1]>>16, store[w+2]>>16, store[w+3]>>16, store[w+4]>>16, store[w+5]>>16, store[w+6]>>16, store[w+7]>>16, count); } sc ++; switch (f) { case 0000: // 0/0/0 - HALT Stop machine and light stop lamp. notImplemented("HALT"); break; /* --------------- */ case 0006: // Place (N) in B in sign and modulus form. if ( (nnn&1) != 0 ) // alpha mode { fetchAlpha(nnn); lsb = lsd; msb = msd; if ( verbosity >= 7 ) fprintf(diag, " Store contents %06X %05X\n", store[nnn], store[nnn-1]); } else { lsb = store[nnn] & 0xFFFFF; // remove spurious sign bit from bottom half msb = store[nnn+1]; } break; case 0002: // 0/d/2 - REPLACE Replace (B) by (N). lsb = store[nnn]; // Place (N) in B in sign and modulus form. msb = lsb & 0x100000; // put in the proper sign digit lsb &= 0xFFFFF; // also remove it from bottom half break; /* --------------- */ case 0005: // 0/1/1 - REPLACE REGISTERS Replace (A), (B), (C) by (N’), (N+2’), // by (N’), (N+2’), (N+ 4’). if ( (nnn&1) != 0 ) // odd value of N indicates fancy alpha stuff ... notImplemented("REPLACE REGISTERS odd address"); // .. which we cannot yet do lsa = store[nnn]; // do we need to be clever with the sign digit ?? msa = store[nnn+1]; lsb = store[nnn+2]; msb = store[nnn+3]; nnn += 4; // drop through to set creg and topcreg /* --------------- */ case 0003: // 0/0/3 - SET RADIX Copy (N) to C. creg = store[nnn] & 0xFFFFF; // ignore the sign digit topcreg = creg & 0xF0000; // selected "radix" of most sig digit topcreg = topcreg | (topcreg>>4) | (topcreg>>8) | (topcreg>>12) | (topcreg>>16); // fprintf(diag, " C = %05X [%05X]\n", creg, topcreg); break; /* --------------- */ case 0004: // 0/1/0 - COPY REGISTERS Copy (A), (B), (C) to N’, N+2’, N + 4’. if ( (nnn&1) != 0 ) // odd value of N indicates fancy alpha stuff ... notImplemented("COPY REGISTERS odd address"); // .. which we cannot yet do store[nnn] = lsa; // do we need to be clever with the sign digit ?? store[nnn+1] = msa; store[nnn+2] = lsb; store[nnn+3] = msb; store[nnn+4] = creg; break; /* --------------- */ case 0014: // 1/d/0 - TABLE LOOK UP Search locations N onwards until a // compartment L is found such that // (L) >= (A) then place L in B. if ( (msa&0x100000) != 0 ) // negative notImplemented("TABLE LOOK UP - negative A"); while ( (w = store[nnn+1] - msa) < 0 || w == 0 && store[nnn] < lsa ) nnn += 2; msb = 0; lsb = nnn; break; case 0010: // 1/d/0 - TABLE LOOK UP if ( msa != 0 ) // negative notImplemented("TABLE LOOK UP - negative or large A"); while ( store[nnn] < lsa ) nnn ++; msb = 0; lsb = nnn; break; /* --------------- */ case 0011: // 1/0/1 - PREPARE FOR DIGIT COLLATION For each of the least significant 10 bits // of N (literal) which is 1 set the // corresponding quartet of B as ‘1111’ // and for each which is 0 set the // corresponding quartet of B as ‘0000’. // Copy bit 11 of N to sign bit of B. lsb = bmask[nnn&0x1F]; msb = bmask[(nnn>>5) & 0x1F] | ((nnn<<9) & 0x100000); break; /* --------------- */ case 0013: // 1/0/3 - INTERCHANGE AREA Interchange (64 + N1) and (64 + N2). // ADDRESSES Used for non-Fast Channel input/output lsd = ((nnn>>6) & 0x3F) + 64; // use a handy variable that gets printed in diagnostics nnn = (nnn & 0x3F) + 64; w = store[lsd]; store[lsd] = store[nnn]; store[nnn] = w; // notImplemented("INTERCHANGE AREA ADDRESSES"); break; /* --------------- */ case 0012: // 1/0/2 - ROUND OFF Add 1 to (A) if Q10 of B >= N and clear B. lsb = 0; d = (msb>>16)&0xF; msb = 0; if ( d < (instr&0x1FFF) ) // leave (A) unchanged break; nnn = instr = 1; // drop through and add this to (A) /* --------------- */ case 0015: // 1/1/1 - ADD LITERAL ADDRESS Add N to (A). if ( creg == 0xFFFFF ) // is this the overflow check in master routine startup ... interrupt(24); // ... if so, cause an overflow interrupt kludge !!! if ( (nnn&017777) != (instr&017777) ) // was there a SMO notImplemented("modified literal on add"); lsa = add5(lsa, instr&017777, creg); // should we cater for SMO? if ( lsa >= 04000000 ) // if it has carried into the top half { msa = add5(msa, 1, topcreg); lsa -= 04000000; } break; /* --------------- */ case 0016: // 1/1/2 - SUBTRACT LITERAL ADDRESS Subtract N from (A). // if ( (nnn&017777) != (instr&017777) ) // notImplemented("modified literal on subtract"); msd = 0x100000; // just the sign digit negates the literal lsd = nnn&017777; // should we cater for SMO? - yes, it seems so if ( creg != 0 // not binary arithmetic && ( qcry(lsd, creg) != 0 // there is out of radix data || qcry(lsa, creg) != 0 ) ) { lsa -= lsd; // give in and use binary arithmetic if ( lsa < 0 ) // borrow needed { lsa += 0x100000; if ( msa-- == 0 ) // change of sign msa = 0x1FFFFF; } } else { complementd(); // converts to numeric form lsa = add5(lsa, lsd, creg); // fprintf(diag, "??? lsa = %06X\n", lsa); if ( lsa >= 0x100000 ) // if it has carried into the top half { msa = add5(msa, 1, topcreg); lsa -= 0x100000; } // fprintf(diag, "??? msa = %06X msd = %06X\n", msa, msd); msa = add5(msa, msd, topcreg); // fprintf(diag, " top C-reg = %06X\n", topcreg); } break; /* --------------- */ case 0017: // 1/1/3 - SELECT LITERAL ADDRESS Select N into A. if ( (nnn&017777) != (instr&017777) ) fprintf(diag, "%5d modified literal on select\n", sc-1); lsa = nnn&017777; // was instr&017777; and then nnn - (sc & 060000) msa = 0; break; /* --------------- */ case 0025: case 0026: case 0027: // 2/1/m - TRANSFER case 0035: case 0036: case 0037: // 3/1/m - COPY m = mval; case 0024: // 2/1/0 - TRANSFER case 0034: // 3/1/0 - COPY pp = (nnn + m) & 077777; // memory address msd = msa; lsd = lsa; complementd(); if ( (pp & 1) != 0 ) storeAlpha(pp); else { store[pp] = lsd; store[pp+1] = msd; } if ( verbosity >= 7 ) fprintf(diag, " Stored %05X -> %d %06X -> %d \n", lsd, pp&077776, msd, pp|1); if ( (f&010) == 0 ) // if TRANSFER { msa = 0; lsa = 0; } break; case 0021: case 0022: case 0023: case 0031: case 0032: case 0033: m = mval; case 0020: // 2/0/m - TRANSFER Transfer (A) to N and clear A. case 0030: // 3/0/m - COPY Copy (A) to N. pp = (nnn + m) & 077777; // memory address msd = msa; lsd = lsa; complementd(); store[pp] = lsd | (msd&0x100000); // copy sign digit from top half if ( (f&010) == 0 ) // if TRANSFER { msa = 0; lsa = 0; } break; /* --------------- */ case 0044: // 4/1/m - ADD Add (N) to (A). case 0054: // 5/1/m - SUBTRACT Subtract (N) from (A). mval = 0; case 0045: case 0046: case 0047: case 0055: case 0056: case 0057: pp = (nnn + mval) & 077777; // memory address if ( (pp & 1) != 0 ) // Add alpha { fetchAlpha(pp); // N.B. pp is the address of the MS half d = -1; // stops loading d lower down } case 0041: case 0042: case 0043: case 0051: case 0052: case 0053: m = mval; case 0040: // 4/0/m - ADD Add (N) to (A). case 0050: // 5/0/m - SUBTRACT Subtract (N) from (A). pp = (nnn + m) & 077777; // memory address if ( d >= 0 ) // if not alpha mode { lsd = store[pp]; if ( d == 0 ) // short form msd = lsd & 0x100000; // sign digit to MS byte else // long form msd = store[pp+1]; lsd &= 0xFFFFF; // remove unwanted sign digit } if ( (f&010) != 0 ) // subtract { if ( d == 1 && verbosity >= 5 ) // long word subtract fprintf(diag, " Checking %s for %s\n", tostr(msa, lsa), tostr(msd, lsd&0xFFFFF)); msd ^= 0x100000; // flip the sign digit } complementd(); lsa = add5(lsa, lsd, creg); if ( lsa >= 0x100000 ) // if it has carried into the top half { msd = add5(msd, 1, topcreg); lsa &= 0xFFFFF; // remove unwanted carry digit } msa = add5(msa, msd, topcreg); break; /* --------------- */ case 0065: case 0066: case 0067: // 6/d/m - SELECT Select (N) into A. m = mval; case 0064: pp = (nnn + m) & 077777; // memory address if ( (pp & 1) != 0 ) fetchAlpha(pp); // N.B. pp is the address of the MS half else { lsd = store[pp] & 0xFFFFF; msd = store[pp+1]; } complementd(); msa = msd; lsa = lsd; break; case 0061: case 0062: case 0063: m = mval; case 0060: // 6/d/m - SELECT Select (N) into A. pp = (nnn + m) & 077777; // memory address lsd = store[pp] & 0xFFFFF; // mask off the sign bit ... msd = store[pp] & 0x100000; // ... and put it in top half // fprintf(diag, " Fetched from %d: %06X %05X\n", pp, msd, lsd); complementd(); msa = msd; lsa = lsd; break; /* --------------- */ case 0074: // 7/d/m - AUGMENT Augment (N) by (A). mval = 0; case 0075: case 0076: case 0077: pp = (nnn + mval) & 077777; // memory address if ( (pp&1) != 0 ) { fetchAlpha(pp); // N.B. pp is the address of the MS half d = -1; // stops loading d lower down } case 0071: case 0072: case 0073: m = mval; case 0070: // 7/d/m - AUGMENT Augment (N) by (A). pp = (nnn + m) & 077777; // memory address if ( d >= 0 ) // skip this if long alpha, D will have been loaded already loadD(d, pp); complementd(); lsd = add5(lsd, lsa, creg); if ( (lsd&0x100000) != 0 ) // carry { msd = add5(msd, 1, topcreg); lsd &= 0xFFFFF; } msd = add5(msd, msa, topcreg); complementd(); if ( d < 0 ) // alpha form storeAlpha(pp); else { if ( d == 0 ) // short form lsd |= msd & 0x100000; // insert sign digit else store[pp+1] = msd; store[pp] = lsd; } break; /* --------------- */ case 0101: case 0102: case 0103: m = mval; case 0100: // 8/0/m - MERGE CONSTANT LENGTH Merge strings of constant length data, // specified by modification registers 1 & // 2 into a block specified by modification // register 3, comparing items and // selecting the lower, until either of the // two input blocks becomes exhausted or // the output block becomes full. Item // length is (N) in binary. pp = (nnn + m) & 077777; // memory address notImplemented("MERGE CONSTANT LENGTH"); break; /* --------------- */ case 0105: case 0106: case 0107: m = mval; case 0104: // 8/1/m - MERGE VARIABLE LENGTH Merge strings of variable length data, // specified by modification registers 1 & // 2 into a block specified by modification // register 3, comparing items and // selecting the lower, until either of the // two input blocks becomes exhausted or // the output block becomes full. Item // length is specified in binary by a single // word entry preceding each data item. pp = (nnn + m) & 077777; // memory address notImplemented("MERGE VARIABLE LENGTH"); break; /* --------------- */ case 0115: case 0116: case 0117: m = mval; // 9/d/m - MULTIPLY UNIFORM RADIX Multiply (A) by (N) and place in AB. case 0114: notImplemented("Long form"); case 0111: case 0112: case 0113: m = mval; case 0110: // 9/d/m - MULTIPLY UNIFORM RADIX Multiply (A) by (N) and place in AB. pp = (nnn + m) & 077777; // memory address notImplemented("MULTIPLY UNIFORM RADIX"); break; /* --------------- */ case 0124: // 10/d/m - MULTIPLY AND ADD Multiply (B) by (N) and add to (A). case 0134: // 11/d/m - MULTIPLY AND SUBTRACT Multiply (B) by (N) and subtract from (A). mval = 0; case 0125: case 0126: case 0127: case 0135: case 0136: case 0137: if ( ((nnn + mval)&1) != 0 ) notImplemented("Long alpha"); case 0121: case 0122: case 0123: case 0131: case 0132: case 0133: m = mval; case 0120: // 10/d/m - MULTIPLY AND ADD Multiply (B) by (N) and add to (A). case 0130: // 11/d/m - MULTIPLY AND SUBTRACT Multiply (B) by (N) and subtract from (A). pp = (nnn + m) & 077777; // memory address loadD(d, pp); if ( (f&010) == 0 ) // MULTIPLY AND ADD { if ( (msb&0x100000) != 0 ) // -ve (B) { msd ^= 0x100000; // negate (N) msb ^= 0x100000; // negate (B) } } else // MULTIPLY AND SUBTRACT { if ( (msb&0x100000) == 0 ) // +ve (B) msd ^= 0x100000; // negate (N) else msb ^= 0x100000; // negate (B) } complementd(); while ( msb + lsb != 0 ) // stop when there is no more to add in { m = 0; // counts the number of additions made so far msf = msd; lsf = lsd; // if ( verbosity >= 9 ) // fprintf(diag, "digit = %d\n", lsb&0xF); while ( ++m < 10 ) { if ( (lsb&0xF) == m ) // (D) = (Q1 of B) x (F) { lsa = add5(lsa, lsd, creg); // if ( verbosity >= 9 ) // fprintf(diag, " adding in %d\n", lsd); if ( lsa >= 0x100000 ) // if it has carried into the top half { msa = add5(msa, 1, topcreg); lsa &= 0xFFFFF; // remove unwanted carry digit } msa = add5(msa, msd, topcreg); } lsd = add5(lsd, lsf, creg); // now add f into d some more if ( lsd >= 0x100000 ) // if it has carried into the top half { msd = add5(msd, 1, topcreg); lsd &= 0xFFFFF; // remove unwanted carry digit } msd = add5(msd, msf, topcreg); } lsb = (lsb>>4) | ((msb<<16)&0xF0000); msb = msb>>4; } break; /* --------------- */ // case 0135: case 0136: case 0137: // m = mval; // 11/d/m - MULTIPLY AND SUBTRACT Multiply (B) by (N) and subtract from (A). // case 0134: // notImplemented("Long form"); // case 0131: case 0132: case 0133: // m = mval; // case 0130: // 11/d/m - MULTIPLY AND SUBTRACT Multiply (B) by (N) and subtract from (A). // pp = (nnn + m) & 077777; // memory address // notImplemented("MULTIPLY AND SUBTRACT"); // break; /* --------------- */ case 0144: // 12/d/m - CONVERT Convert (N) to the radix (C) and place mval = 0; case 0145: case 0146: case 0147: if ( ((nnn + mval)&1) != 0 ) d = -1; case 0141: case 0142: case 0143: m = mval; case 0140: // 12/d/m - CONVERT Convert (N) to the radix (C) and place // in A using table held in (A) onwards. pp = (nnn + m) & 077777; // memory address lsb = store[pp]; msb = 0; // in case mode iws not long if ( d == 0 ) // short form d = lsb & 0x100000; // keep sign bit else if ( d >= 0 ) // long form { msb = store[pp+1]; // (N) is placed in B in sign and modulus form. d = msb & 0x100000; // keep sign bit msb &= 0xFFFFF; // remove any unwanted sign bit } else // alpha { fetchAlpha(pp); // value into D d = msd; // sign digit lsb = lsd; // value converted from alpha } lsb &= 0xFFFFF; // remove any unwanted sign bit i = lsa, // x, the address of the first equivalent in A, is placed in OI. lsa = msa = 0; // A is cleared. while ( (msb|lsb) != 0 ) // if (B) is zero step 8 { w = lsb & 15; // bottom digit lsd = store[i++]; msd = store[i++]; while ( --w >= 0 ) { lsa = add5(lsd, lsa, creg); if ( (lsa&0x100000) != 0 ) // carry { msa = add5(msa, 1, topcreg); lsa &= 0xFFFFF; } msa = add5(msd, msa, topcreg); } lsb = ((lsb>>4) | (msb<<16)) &0xFFFFF; msb = msb>>4; } if ( d != 0 ) { if ( creg != 0 ) // not sure what to do about radix here, probably straight binary negate { if ( lsa == 0 ) // avoid borrow by making this a special case msa = add5(0x1FFFFF - topcreg - msa, 1, topcreg); else { msa = 0x1FFFFF - topcreg - msa; lsa = add5(0xFFFFF - creg - lsa, 1, creg); } } else if ( lsa == 0 ) // no carry between halves msa = (-msa) & 0x1FFFFF; // so just negate the top half else { lsa = (-lsa) & 0xFFFFF; // this would be bound to cary msa = msa ^ 0x1FFFFF; // complement = -n - 1 } } break; /* --------------- */ case 0154: // 13/d/m - DIVIDE UNIFORM RADIX Divide (AB) by (N) leaving quotient in A mval = 0; case 0155: case 0156: case 0157: if ( ((nnn + mval)&1) != 0 ) notImplemented("Long alpha"); case 0151: case 0152: case 0153: m = mval; case 0150: // 13/d/m - DIVIDE UNIFORM RADIX Divide (AB) by (N) leaving quotient in A // and remainder in B. pp = (nnn + m) & 077777; // memory address if ( creg != 0 ) notImplemented("DIVIDE UNIFORM RADIX non-binary"); loadD(d, pp); complementd(); if ( msd != 0 ) notImplemented("DIVIDE UNIFORM RADIX big or -ve denominator"); if ( msa != 0 || lsa != 0 || msb >= 0x1000 ) notImplemented("DIVIDE UNIFORM RADIX big or -ve numerator"); w = (msb<<16) | (lsb>>4); // numerator d = w / lsd; // quotient w = w % lsd; // remainder lsa = d & 0xFFFFF; msa = d >> 20; lsb = (lsb&15) | ((w&0xFFFF)<<4); msb = w >> 16; break; /* --------------- */ case 0164: // 14/d/m - REPLACE SELECTED BITS Replace bits of (N) specified by (B) with mval = 0; // corresponding bits of the sign and case 0165: case 0166: case 0167: // modulus form of (A). pp = (nnn + mval) & 077777; // memory address if ( (pp&1) != 0 ) // alpha form { d = 2; // marker for return to store fetchAlpha(pp); // loads D with numberical equivalent of value from store store[pp] = lsd | msd; // looks like short form in memory } case 0161: case 0162: case 0163: m = mval; case 0160: // 14/d/m - REPLACE SELECTED BITS Replace bits of (N) specified by (B) with // corresponding bits of the sign and msd = msa; lsd = lsa; // modulus form of (A). pp = (nnn + m) & 077777; // memory address complementd(); // d now holds the sign and modulus form of (A) if ( d == 1 ) // long form - not alpha { w = msb & 0x1FFFFF; store[pp+1] &= w^0x1FFFFF; // knock out the bits that are to be replaced ... store[pp+1] |= w & msd; // ... and slot in the new stuff w = lsb & 0xFFFFF; } else { w = (msb&0x1FFFFF) + lsb; // sign digit needs to be incorporated for first word lsd |= msb&0x1FFFFF; } store[pp] &= w^0x1FFFFF; // knock out the bits that are to be replaced ... store[pp] |= w & lsd; // ... and slot in the new stuff if ( d == 2 ) // alpha form, convert the result from numeric { loadD(0, pp); // load short form word storeAlpha(pp); // and put the alpha form back into memory } break; /* --------------- */ case 0174: // 15/d/m - COLLATE AND ADD Collate (N) with (B) and add to (A). mval = 0; case 0175: case 0176: case 0177: pp = (nnn + mval) & 077777; // memory address if ( tag[pp&077776] == 15 // is this the lockout check in master start-up && store[31] != 0 ) // or are now ready for lockout interrupts interrupt(16); // generate lockout interrupt kludge !!! if ( (pp & 1) != 0 ) // Add alpha { fetchAlpha(pp); // N.B. pp is the address of the MS half d = -1; // stops loading d lower down } case 0171: case 0172: case 0173: m = mval; case 0170: // 15/d/m - COLLATE AND ADD Collate (N) with (B) and add to (A). pp = (nnn + m) & 077777; // memory address if ( d >= 0 ) // not alpha loadD(d, pp); lsd &= lsb; msd &= msb; // collation before switch from sign and modulus complementd(); lsa = add5(lsd, lsa, creg); if ( (lsa&0x100000) != 0 ) // carry has occurred { msa = add5(msa, 1, topcreg); msa = add5(msd, msa, topcreg); lsa &= 0xFFFFF; } else msa = add5(msd, msa, topcreg); break; /* --------------- */ case 0204: // 16/1/0 - COMPARE Compare strings S1 and S2 of alpha // data where S1 starts at N and S2 at // (A). Successive pairs of words are // compared until a difference is found or // the end of the strings is reached. The // next instruction is obeyed if S2 is > S1, // one instruction is skipped if S2 = S1, // and two instructions are skipped if S2 // is < S1. (Only available on LEO 360 // and 326). notImplemented("COMPARE"); break; /* --------------- */ case 0210: // 17/0/0 - SPECIAL SELECT Select (N’) into A if N is even, otherwise // select (N+1’) into A. No // change is made between sign and // modulus and sign and complement form. notImplemented("SPECIAL SELECT"); break; /* --------------- */ case 0214: // 17/1/0 - SPECIAL COPY Copy (A) to N’ if N is even, otherwise // copy (A) to N+1’. No change is made // between sign and modulus and sign // and complement form. notImplemented("SPECIAL COPY"); break; /* --------------- */ case 0221: // 18/0/1 - SHIFT ARITHMETIC Shift (A) arithmetically. d = (15 - (topcreg&15)) << 16; // padding quartet in case of right shift of -ve A // d will be zero for a logical shift case 0220: // 18/0/0 - SHIFT LOGICAL Shift (A) logically. m = msa & 0x100000; // keep the sign digit msa &= 0xFFFFF; // and remove it from msa if ( (nnn & 0x1000) == 0 ) // left shift { nnn &= 0x1F; // just keep bottom 5 bits while ( nnn > 0 ) // simple implementation { msa = ((msa<<4) & 0xFFFF0) + (lsa>>16); lsa = (lsa<<4) & 0xFFFF0; // one quartet now shifted nnn --; } msa |= m; // put the sign digit back } else { nnn = (-nnn) & 0x1F; // just keep bottom 5 bits if ( m == 0 ) // is zero sign digit, i.e. >= 0 d = 0; // padding quartet while ( nnn > 0 ) // simple implementation { lsa = ((lsa>>4) + (msa<<16) & 0xFFFFF); msa = (msa>>4) + d; // one quartet now shifted nnn --; // note: d is the padding quartet } msa |= m; // put the sign digit back } break; /* --------------- */ case 0222: // 18/0/2 - SCALE NUMERATOR Shift (AB) so that the most significant // non-zero digit of (AB) is in Q9 of A. notImplemented("SCALE NUMERATOR"); break; /* --------------- */ case 0223: // 18/0/3 - SHIFT BINARY Binary left shift (A). m = msa & 0x100000; // keep the sign digit nnn &= 0xFFF; // just keep bottom 12 bits while ( nnn > 0 ) // simple implementation { msa = ((msa<<1) & 0xFFFFE) + (lsa>>19); lsa = ((lsa<<1) & 0xFFFFE); nnn --; } msa |= m; // put the sign digit back break; /* --------------- */ /* --------------- */ case 0225: // 18/1/1 - SHIFT ARITHMETIC Shift (AB) arithmetically. m = (15 - (topcreg&15)) << 16; // padding quartet in case of right shift of -ve A // m will be zero for a logical shift case 0224: // 18/1/0 - SHIFT LOGICAL Shift (AB) logically. d = m; // use d for compatibility with single length shift m = msa & 0x100000; // keep the sign digit msa &= 0xFFFFF; // and remove it from msa w = msb & 0x100000; // keep the sign digit msb &= 0xFFFFF; // and remove it from msb if ( (nnn & 0x1000) == 0 ) // left shift { nnn &= 0x1F; // just keep bottom 5 bits while ( nnn > 0 ) // simple implementation { msa = ((msa<<4) & 0xFFFF0) + (lsa>>16); lsa = ((lsa<<4) & 0xFFFF0) + (msb>>16); msb = ((msb<<4) & 0xFFFF0) + (lsb>>16); lsb = (lsb<<4) & 0xFFFF0; // one quartet now shifted nnn --; } } else { nnn = (-nnn) & 0x1F; // just keep bottom 5 bits if ( m == 0 ) // is zero sign digit, i.e. >= 0 d = 0; // padding quartet is zero for +ve operand while ( nnn > 0 ) // simple implementation { lsb = ((lsb>>4) + (msb<<16) & 0xFFFFF); msb = ((msb>>4) + (lsa<<16) & 0xFFFFF); lsa = ((lsa>>4) + (msa<<16) & 0xFFFFF); msa = (msa>>4) + d; // one quartet now shifted nnn --; } } msa |= m; msb |= w; // put the sign digits back break; /* --------------- */ case 0226: // 18/1/2 - SCALE DENOMINATOR Shift (AB) so that the most significant // non-zero digit of (AB) is in Q9 of A. notImplemented("SCALE DENOMINATOR"); break; /* --------------- */ case 0227: // 18/1/3 - SHIFT BINARY Binary left shift (AB). m = msa & 0x100000; // keep the sign digit d = msb & 0x100000; // keep the sign digit msb &= 0xFFFFF; // and stop it getting shifted into lsa nnn &= 0xFFF; // just keep bottom 12 bits while ( nnn > 0 ) // simple implementation { msa = ((msa<<1) & 0xFFFFE) + (lsa>>19); lsa = ((lsa<<1) & 0xFFFFE) + (msb>>19); msb = ((msb<<1) & 0xFFFFE) + (lsb>>19); lsb = (lsb<<1) & 0xFFFFE; // one bit now shifted nnn --; } msa |= m; // put the sign digit back msb |= d; // put the sign digit back break; /* --------------- */ case 0230: // 19/0/0 - OUTPUT Output one block to route N1. done19 = 1; // we must be in master mode now w = store[(nnn&63)+64] & 077777; if ( verbosity >= 2 ) fprintf(diag, "Trying to output on route %02o from data at %d (type = %d)\n", nnn&63, w, dvty[nnn&0xFF]); if ( dvty[nnn&63] == 99 ) // pseudo type number for typewriter typeout(nnn&63, w); else if ( ((dvty[nnn&0xFF] + 1) & 0xFE) == 8 ) // device type = 7 or 8, i.e. MT mtio(nnn&0x3F, 0); else if ( dvty[nnn&0xFF] == 9 ) // device type = 9 i.e. printer lpop(nnn&0x3F, 0); else notImplemented("OUTPUT"); setengaged(nnn&63, 200); break; /* --------------- */ case 0231: // 19/0/1 - INPUT ONE BLOCK / Input one block from route N1./ // RESET TIMER Reset to zero. (Timer only) done19 = 1; // we must be in master mode now if ( dvty[i = nnn&63] == 5 ) // paper tape reader readptr(i); else if ( ((dvty[i]-1)&0xFE) == 6 ) // 7 or 8 = mag tape mtio(nnn&0x3F, 1); else notImplemented("INPUT ONE BLOCK/RESET TIMER"); setengaged(nnn&63, 200); break; /* --------------- */ case 0232: // 19/0/2 - RUN BACK Run back to last block mark on route N1. case 0233: // 19/0/3 - RUN FORWARD Run forward to next mark on route N1. case 0234: // 19/1/0 - STEP BACK Step back one block on route N1. case 0235: // 19/1/1 - REWIND Rewind route N1 ready to read/write first block done19 = 1; // we must be in master mode now mtio(nnn&0x3F, f&7); setengaged(nnn&63, 200); break; /* --------------- */ case 0236: // 19/1/2 - UNLOAD Unload route N1 and set route to manual. done19 = 1; // we must be in master mode now if ( ((dvty[i = nnn&0xFF] + 1) & 0xFE) == 8 ) // device type = 7 or 8, i.e. MT mtio(i, 6); else if ( dvty[i] == 9 ) // Anelex printer -- output 4 blank lines either side of a marker write(dvdv[i], "\n\n--------------------------------------------------\n\n", 54); else // ignore manual of paper tape etc TEMP !!! ? fprintf(diag, "Unload %02o to manual ignored\n", nnn&0xFF); // notImplemented("UNLOAD"); break; /* --------------- */ case 0237: // 19/1/3 - INPUT FIRST WORD Input first word of block and run // forward to next block end without // further transfer of information // SET ROUTE N1 TO MANUAL (non-magnetic tape media) // Set route N1 to manual done19 = 1; // we must be in master mode now if ( ((dvty[nnn&0xFF] + 1) & 0xFE) == 8 ) // device type = 7 or 8, i.e. MT mtio(nnn&0x3F, 257); // don't input the whole block else // SET ROUTE N1 TO MANUAL fprintf(diag, "Set %02o to manual ignored\n", nnn&0xFF); // but not at the moment setengaged(nnn&63, 200); break; // notImplemented("INPUT FIRST WORD"); /* --------------- */ case 0241: case 0242: case 0243: m = mval; case 0240: // 20/0/m - ADD FLOATING POINT Add (N) to (A*). pp = (nnn + m) & 077777; // memory address notImplemented("ADD FLOATING POINT"); break; /* --------------- */ case 0245: case 0246: case 0247: m = mval; case 0244: // 20/1/m - SUBTRACT FLOATING POINT Subtract (N) from (A*). pp = (nnn + m) & 077777; // memory address notImplemented("SUBTRACT FLOATING POINT"); break; /* --------------- */ case 0251: case 0252: case 0253: m = mval; case 0250: // 21/0/m - TRANSFER FLOATING POINT Transfer (A*) to N. pp = (nnn + m) & 077777; // memory address notImplemented("TRANSFER FLOATING POINT"); break; /* --------------- */ case 0255: case 0256: case 0257: m = mval; case 0254: // 21/1/m - COPY FLOATING POINT Copy (A*) to N. pp = (nnn + m) & 077777; // memory address notImplemented("COPY FLOATING POINT"); break; /* --------------- */ case 0261: case 0262: case 0263: m = mval; case 0260: // 22/0/m - MULTIPLY FLOATING POINT Multiply (A*) by (N) and place in A*. pp = (nnn + m) & 077777; // memory address notImplemented("MULTIPLY FLOATING POINT"); break; /* --------------- */ case 0265: case 0266: case 0267: m = mval; case 0264: // 22/1/m - DIVIDE FLOATING POINT Divide (A*) by (N) and place in A*. pp = (nnn + m) & 077777; // memory address notImplemented("DIVIDE FLOATING POINT"); break; /* --------------- */ case 0272: // 23/0/2 - ENTER MASTER ROUTINE Place SC + 1 in bits 1 to 15 of N and // (T) in bits 17 to 20 set and T to 14. // Change sequence to N + 1. store[nnn] = (tagreg << 16) + sc; // return address ?? division // fprintf(diag, "Return address = %d\n", store[nnn] & 077777); if ( verbosity >= 2 ) fprintf(diag, "%5d ENTER MASTER to %5d (%d,%d)\n", sc-1, nnn, nnn&8191, nnn-lastoverlay); tagreg = 14; if ( nnn < 200 && done19 == 0 ) // emulation needed for master routine function master(nnn); // enter master routine whose action is emulated here else sc = nnn + 1; // and jump break; /* --------------- */ case 0273: // 23/0/3 - SELECT TAG Select tag of N into A clearing rest of A. lsa = tag[nnn]; // tags not checked at present msa = 0; break; /* --------------- */ case 0275: case 0276: case 0277: m = mval; // add in the modifier case 0274: // 23/1/m - COPY INTO TAG Copy (A) into tag of N. // if ( nnn+m >= 32768 ) // illegal("Tag modifier out of range", nnn+m); tag[i = (nnn+m)&0x7FFE] = lsa & 0xF; // tags not checked at present tag[i+1] = lsa & 0xF; // set tag on both half words to make checking easier break; // when we get round to it /* --------------- */ case 0300: // 24/0/0 - MODIFY ADDRESS OF NEXT Search locations N, (|N|), (|(|N|)|), etc. // INSTRUCTION WITH A POSITIVE until a location with positive contents is // MODIFIER AND CURRENT found. Modify address of next // DIVISION NUMBER instruction by bits 1 to 15 of that // location after adding the division // number from SC. while ( ((nnn = store[nnn&077777])&0x100000) != 0 ) // follow chain of links with sign set if ( verbosity >= 9 ) fprintf(diag, " Modification got to %06X %d\n", nnn, nnn&077777); if ( verbosity >= 9 ) fprintf(diag, " Modification ended at %06X %d\n", nnn, nnn); smoval = nnn&077777; break; /* --------------- */ case 0301: // 24/0/1 - MODIFY ADDRESS OF NEXT Modify address of next instruction by // INSTRUCTION (N) after adding the division number // from SC. smoval = store[nnn] & 077777; // use LS 15 bits break; /* --------------- */ case 0302: // 24/0/2 - SELECT LITERAL AND DIVISION Select N and division number from SC // NUMBER into A. lsa = nnn; // seems that 24/0/3 is allowed to affect this msa = 0; break; /* --------------- */ case 0303: // 24/0/3 - MODIFY ADDRESS OF NEXT Modify address of next instruction by // INSTRUCTION SUPPRESSING (N) suppressing current division number // DIVISION NUMBER smoval = (store[nnn]&077777) - (sc & 060000); // eliminate division number by subtracting it before addition in main loop break; /* --------------- */ case 0304: // 24/1/0 - UNCONDITIONAL SEQUENCE CHANGE Change sequence to N. nnn &= 32767; // mask off any top tag bits if ( verbosity >= 4 ) fprintf(diag, "%5d SEQUENCE CHANGE to %5d (%d,%d)\n", sc-1, nnn, nnn&8191, nnn-lastoverlay); sc = nnn; break; /* --------------- */ case 0305: // 24/1/1 - SET MODIFICATION GROUP Set bits 14 and 15 of indicator register // to bits 14 and 15 of (N). indreg &= 017777; // remove bits 14 and 15 groupref = (store[nnn&077777] & 060000) >> 10; indreg |= groupref<<10; // and put in the new settings showIndicators(0); // notImplemented("SET MODIFICATION GROUP"); break; /* --------------- */ case 0306: // 24/1/2 - MODIFY ADDRESS OF NEXT Search locations N, (|N|), (|(|N|)|), etc. // INSTRUCTION WITH A POSITIVE until a location with positive contents is // MODIFIER SUPPRESSING found. Modify address of next // DIVISION NUMBER instruction by bits of that location // suppressing the division number from SC. while ( ((nnn = store[nnn&077777])&0x100000) != 0 ) // follow chain of links with sign set if ( verbosity >= 9 ) fprintf(diag, " Modification got to %06X %d\n", nnn, nnn&077777); if ( verbosity >= 9 ) fprintf(diag, " Modification ended at %06X %d\n", nnn, nnn); smoval = (nnn&077777) - (sc & 060000); // eliminate division number by subtracting it before addition in main loop break; /* --------------- */ case 0307: // 24/1/3 - STORE INDICATORS Copy (I) to N nnn &= 32767; // mask off any top tag bits store[nnn] = indreg; break; /* --------------- */ case 0310: // cannot see that this instruction makes sense notImplemented("Stepping mod reg 0"); /* --------------- */ case 0270: // 23/0/0 - STEP ON AND TEST Step on and test indirect modifier by N. // If it equals end value skip two // instructions, if not skip one. w = store[sc++] & 0xFFFFF; // address of simulated modifier in bits 1-15 if ( (w&1) != 0 ) notImplemented("STEP ON AND TEST on odd address"); m = (w - groupref) / 2; // causes next section to address the simulated modifier // drop through case 0311: case 0312: case 0313: // 25/0/m - STEP ON AND TEST Step on and test modification register // MODIFICATION REGISTER by N. If it equals end value skip one nnn &= 0x1FFF; // instruction. if ( nnn >= 0x1000 ) // if -ve step -- step is sign and modulus nnn = 0x1000 - nnn; // -ve increment in nnn w = (store[groupref + m + m] + nnn); // was this & a mistake & 0x7FFF; // beware -ve values on 15-bit arithmetic // if ( nnn != 0 && sc == 13156 && (store[groupref + m + m + 1] - w) % nnn != 0 ) // will never finish if repeated // illegal("Infinite loop danger", nnn); // temporary !!! if ( verbosity >= 9 ) fprintf(diag, "Modifier %d changed from %06X to %06X, limit = %06X\n", m, store[groupref + m + m], w, store[groupref + m + m + 1]); store[groupref + m + m] = w; if ( ((store[groupref + m + m + 1] - w) & 0x7FFF) == 0 ) // test against end value !!!!! sc ++; // skip one instruction // !!!!! this masking is probably not really correct, but makes practical stuff work break; /* --------------- */ case 0314: // 25/1/0 - SET INDICATORS Where bits of N are '1' set // corresponding bits of indicator register. indreg &= 060000; // retain bits 14 and 15 indreg |= nnn & 017777; // and put in the new settings showIndicators(0); break; /* --------------- */ case 0315: // 25/1/1 - CLEAR INDICATORS Where bits of N are '1' reset // corresponding bits of indicator register. indreg &= (nnn ^ 017777) | 060000; showIndicators(0); break; /* --------------- */ case 0316: // 25/1/2 - INTERROGATE INDICATORS Collate indicator register with N and nnn &= 0xFFF; if ( nnn != 0 ) // if this is not just a quick clear accumulator { if ( indflag > 0 ) indflag --; // just let a few go by before asking for input if ( indflag == 0 // end of suppression of input of indicator register && done19 != 0 // manual setting of indicator reg for simulation of operator ) { if ( opsock < 0 ) // not operating via ops panel LeoConsole.java - need invitation { fprintf(stderr, indregprompt, sc-1, indreg, nnn, intreq, count); fflush(diag); // so that the file can be seen in another window } readkbd(2); // send command zz if there is nothing from the operator opscometc(); } if ( verbosity >= 2 ) fprintf(diag, "%5d reading INDICATORS %05X (mask %05X) after %d instructions\n", sc-1, indreg, nnn, count); showIndicators(0); } lsa = indreg & nnn; // copy result to A. -- can only 12 bits be set ? msa = 0; // notImplemented("INTERROGATE INDICATORS"); break; /* --------------- */ case 0317: // 25/1/3 - CONDITIONAL HALT Collate indicator register with N and // Halt the computer if result non-zero. if ( (nnn & indreg & 0xFFF) != 0 ) notImplemented("CONDITIONAL HALT"); break; // never obey the next bit -- but keep it just in case printf("%5d CONDITIONAL HALT after %d instructions\n", sc-1, count); readkbd(1); if ( buff[0] == 'x' ) notImplemented("CONDITIONAL HALT"); break; /* --------------- */ case 0320: // 26/0/0 - ENTER SUBROUTINE Place (SC) in N and change sequence nnn &= 32767; // to N + 1. if ( verbosity >= 4 ) fprintf(diag, "%5d ENTER SUBROUTINE to %5d (%d,%d)\n", sc-1, nnn, nnn&8191, nnn-lastoverlay); store[nnn] = sc; // return address ?? division sc = nnn + 1; // and jump if ( nnn == turnOff ) { kverbosity = verbosity; if ( verbosity >= 4 ) fprintf(diag, " .......... enter waiting loop\n"); verbosity = 0; // suppress diagnostics in time waster } break; /* --------------- */ case 0321: // 26/0/1 - LEAVE SUBROUTINE Change sequence to (N). w = store[nnn]; if ( (w&0x8000) != 0 ) // assume one block of store changed from 0xFF8000 13 Jan 2015 illegal("LEAVE SUBROUTINE to big address", store[nnn]); if ( nnn == turnOff ) { verbosity = kverbosity; if ( verbosity >= 4 ) fprintf(diag, " .......... leave waiting loop\n"); kverbosity = -1; } w &= 32767; // mask off any top tag bits if ( verbosity >= 2 ) fprintf(diag, "%5d LEAVE SUBROUTINE to %5d (%d,%d) after %d instructions\n", sc-1, w, w&8191, w-lastoverlay, count); sc = w; // master routine often has NZ in top bits -- we have lost bad switch test break; /* --------------- */ case 0322: // 26/0/2 - ENTER PRIORITY CONTROL Place (SC) in N bits 1 to 15 and (T) in // N bits 17 to 20 and change sequence // to N + 1. if ( nnn < 160 && done19 == 0 ) svc(nnn); // implements just those master routine calls used by IT else // genuine call with master routine operational (?) { tagreg = 14; indreg &= 0x1EFFF; store[nnn] = (tagreg << 16) + sc - 1; // return address -- see Vol I sect 17.1.6 note (ii)(b) sc = nnn + 1; } break; // notImplemented("ENTER PRIORITY CONTROL"); /* --------------- */ case 0323: // 26/0/3 - LEAVE MASTER ROUTINE Replace (T) by bits 17 to 20 of (N). // Set bit 13 of I and change sequence to nnn = store[nnn]; // bits 1 to 15 of (N). indreg |= 010000; // assuming that I means the indicator register tagreg = (nnn>>16) & 017; // mask just in case nnn &= 32767; // mask off any tag bits if ( verbosity >= 2 ) fprintf(diag, "%5d LEAVE MASTER to %5d (%d,%d)\n", sc-1, nnn, nnn&8191, nnn-lastoverlay); sc = nnn; break; /* --------------- */ case 0324: // 26/1/0 - TEST ROUTE Test route specified by N literal. i = nnn&63; // route number if ( verbosity >= 2 ) fprintf(diag, "%5d Testing route %02o (%X) status %d type %d after %d instructions ( = previous + %d)\n", sc-1, i, nnn, dvstat[i], dvty[i], count, count - zzz); zzz = count; if ( dvty[i] >= 0 ) // will report closed if no device exists on this route { if ( dvstat[i] >= 0 ) // if not set to manual .... if ( count < dveng[i] ) // device is still engaged { if ( intreq < 0 // no interrupt is currently pending || dveng[i] < intreq ) intreq = dveng[i]; // this device is the next interrupt if ( verbosity >= 9 ) fprintf(diag, "%5d route %02o busy: intreq = %d dveng[%d] = %d count = %d\n", sc-1, i, intreq, i, dveng[i], count); sc += 1; // return device busy } else // device is ready { dveng[i] = -1; // clear any engaged status - not sure this is necessary sc += 4; // return available } else if ( ((dvty[i]+1)&0xFE) == 8 && --dvstat[i] < -5 ) // if repeatedly tested MT { printf("%5d Mounting new tape on route %02o (dvdv = %d)\n", sc-1, i, dvdv[i]); fprintf(diag, "%5d Mounting new tape on route %02o (dvdv = %d)\n", sc-1, i, dvdv[i]); sc += 4; // mark it as available while ( dvdv[i] < 0 ) // no UNIX device allocated -- persist until we have one { printf("Need tape mounting on route %02o\n", i); buff[3] = 'M'; // M for mag tape -- shows need for tape mount buff[4] = i; readkbd(3); // give chance for a mount command opscometc(); // keep going until abandoned by x or successful mount mtblk[i] = 0; // size of block behind tape head -- start of tape dvstat[i] = 0; // set up as normal } } } break; // notImplemented("TEST ROUTE"); /* --------------- */ case 0325: case 0326: case 0327: // 0324: // 26/1/m - SET MODIFICATION REGISTER Copy (N) to modification register. store[w = groupref + m + m] = store[nnn]; store[w+1] = store[nnn+1]; break; /* --------------- */ case 0330: // 27/0/0 - TEST SHORT ACCUMULATOR = 0 Test (A) and change sequence to N if nnn &= 32767; // (A) equal to 0. if ( msa == 0 && lsa == 0 ) { if ( verbosity >= 4 ) fprintf(diag, "%5d CONDITIONAL JUMP to %5d (%d,%d)\n", sc-1, nnn, nnn&8191, nnn-lastoverlay); sc = nnn; } break; /* --------------- */ case 0331: // 27/0/1 - TEST ACCUMULATOR non 0 Test (A) and change sequence to N if // (A) not equal to 0. if ( msa != 0 || lsa != 0 ) { nnn &= 32767; // mask off any dubious SMO bits if ( verbosity >= 4 ) fprintf(diag, "%5d CONDITIONAL JUMP to %5d (%d,%d)\n", sc-1, nnn, nnn&8191, nnn-lastoverlay); sc = nnn; } break; /* --------------- */ case 0336: // 27/1/2 - TEST LONG ACCUMULATOR Test (AB) and change sequence to N if // +ve/0 (AB) positive or 0. case 0332: // 27/0/2 - TEST SHORT ACCUMULATOR Test (A) and change sequence to N if // +ve/0 (A) positive or 0. if ( (msa & 0x100000) == 0 ) { nnn &= 32767; // mask off any dubious SMO bits if ( verbosity >= 4 ) fprintf(diag, "%5d CONDITIONAL JUMP to %5d (%d,%d)\n", sc-1, nnn, nnn&8191, nnn-lastoverlay); sc = nnn; } break; /* --------------- */ case 0337: // 27/1/3 - TEST LONG ACCUMULATOR -ve Test (AB) and change sequence to N if // drop through // (AB) negative. /* --------------- */ case 0333: // 27/0/3 - TEST ACCUMULATOR -ve Test (A) and change sequence to N if if ( (msa & 0x100000) != 0 ) // (A) negative. { nnn &= 32767; // mask off any dubious SMO bits if ( verbosity >= 4 ) fprintf(diag, "%5d CONDITIONAL JUMP to %5d (%d,%d)\n", sc-1, nnn, nnn&8191, nnn-lastoverlay); sc = nnn; } break; /* --------------- */ case 0334: // 27/1/0 - TEST LONG ACCUMULATOR = 0 Test (AB) and change sequence to N if if ( (msa | lsa | msb | lsb) == 0 ) // (AB) equal to 0. { nnn &= 32767; // mask off any dubious SMO bits if ( verbosity >= 4 ) fprintf(diag, "%5d CONDITIONAL JUMP to %5d (%d,%d)\n", sc-1, nnn, nnn&8191, nnn-lastoverlay); sc = nnn; } break; /* --------------- */ case 0335: // 27/1/1 - TEST LONG ACCUMULATOR Test (AB) and change sequence to N if // non 0 (AB) not equal 0. if ( (msa | lsa | msb | lsb) != 0 ) // (AB) equal to 0. { nnn &= 32767; // mask off any dubious SMO bits if ( verbosity >= 4 ) fprintf(diag, "%5d CONDITIONAL JUMP to %5d (%d,%d)\n", sc-1, nnn, nnn&8191, nnn-lastoverlay); sc = nnn; } break; /* --------------- */ case 0342: // 28/0/2 - UNPACK FIXED Unpack data in alpha octet form in a // FIELD DATA fixed field layout in long compartments store[1] = sc; // starting at N according to table entry // starting at (A). if ( verbosity >= 3 ) fprintf(diag, "%5d UNPACK FIXED [%06X %05X] starting at %d using table at %d -- %06X %05X=%d\n", sc-1, store[nnn+1], store[nnn], nnn, lsa, store[lsa+1], store[lsa], store[lsa]&0xFFFF); m = 0; w = nnn; while ( m < 300 ) // copy a likely slab of data into a convenient form { lsd = store[w++]; msd = store[w++]; buff[m++] = (msd >>12) & 0xFF; // top char = Q10 and Q9 buff[m++] = (msd >>4) & 0xFF; // next char = Q8 and Q7 buff[m++] = ((msd <<4) & 0xFF) | ((lsd >>16) & 0xF); // next char = Q6 and Q5 buff[m++] = (lsd >>8) & 0xFF; // next char = Q4 and Q3 buff[m++] = lsd & 0xFF; // last char = Q2 and Q1 } buff[m++] = BLOCKEND; // crude protection against running off the end buff[m++] = LINEEND; buff[m++] = BLOCKEND; // crude protection against running off the end buff[m++] = LINEEND; if ( verbosity >= 3 ) fprintf(diag, " buff = %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X - %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X - %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", buff[0], buff[1], buff[2], buff[3], buff[4], buff[5], buff[6], buff[7], buff[8], buff[9], buff[10], buff[11], buff[12], buff[13], buff[14], buff[15], buff[16], buff[17], buff[18], buff[19], buff[20], buff[21], buff[22], buff[23], buff[24], buff[25], buff[26], buff[27], buff[28], buff[29]); if ( verbosity >= 3 ) fprintf(diag, " buff = %c %c %c %c %c %c %c %c %c %c - %c %c %c %c %c %c %c %c %c %c - %c %c %c %c %c %c %c %c %c %c\n", alpha[buff[0]], alpha[buff[1]], alpha[buff[2]], alpha[buff[3]], alpha[buff[4]], alpha[buff[5]], alpha[buff[6]], alpha[buff[7]], alpha[buff[8]], alpha[buff[9]], alpha[buff[10]], alpha[buff[11]], alpha[buff[12]], alpha[buff[13]], alpha[buff[14]], alpha[buff[15]], alpha[buff[16]], alpha[buff[17]], alpha[buff[18]], alpha[buff[19]], alpha[buff[20]], alpha[buff[21]], alpha[buff[22]], alpha[buff[23]], alpha[buff[24]], alpha[buff[25]], alpha[buff[26]], alpha[buff[27]], alpha[buff[28]], alpha[buff[29]]); m = 0; // pointer along buff when reading data d = lsa; // pointer along table entries // Loop steps through the successive table entries msd = 0; // ensure first iteration - normally last table entry processed while ( (msd&0x100000) == 0 ) // look for sign digit in last table entry { lsd = store[d++]; // pick up table entry msd = store[d++]; lsa = msa = 0; // data accumulated here w = 0; // terminator char of last conversion i = 0; // sign of result f = msd & 0xF; // Q6 = Number of characters in item if ( f == 0 ) f = 16; if ( verbosity >= 3 ) fprintf(diag, " Hope to unpack %d chars into %d -", f, lsd&0xFFFF); if ( (msd&0x10000) != 0 ) // alpha data { if ( verbosity >= 3 ) fprintf(diag, " alpha"); while ( --f >= 0 ) { msa = ((msa << 8) | (lsa >> 12)) & 0xFFFFF; lsa = ((lsa << 8) | buff[m++]) & 0xFFFFF; } msd |= 0x30000; // ensure that it is stored long } else // numeric data { if ( verbosity >= 3 ) fprintf(diag, " numeric"); while ( --f >= 0 ) { w = buff[m++]; if ( (((w&0x30)-0x10)&0xE0) == 0 ) // check for sign - true if -ve if ( msa == 0 && lsa == 0 ) // this is the first sig char i = 0x100000; else w = ((w>>4)&3) + 9; // gives result 10 or 11 msa = ((msa << 4) | (lsa >> 16)) & 0xFFFFF; lsa = ((lsa << 4) | (w&0xF)) & 0xFFFFF; } } if ( (msd&0x40000) == 0 ) // item not to be signed i = 0; if ( (msd&0x20000) != 0 ) { if ( verbosity >= 3 ) fprintf(diag, " long\n"); store[a = lsd&0xFFFF] = lsa; store[a+1] = msa | i; // put in the sign digit } else { if ( verbosity >= 3 ) fprintf(diag, " short\n"); store[a = lsd&0xFFFF] = lsa | i; // put in the sign digit } if ( verbosity >= 3 ) fprintf(diag, " Stored %06X %05X into %d - terminated by %02X\n", msa, lsa, a, w); } lsa = msa = msb = lsb = 0; break; /* --------------- */ case 0343: // 28/0/3 - UNPACK VARIABLE Unpack data in alpha octet form in a // FIELD DATA variable field layout in long store[1] = sc; // compartments starting at N according // to the number of items specified by // table entry (A). if ( verbosity >= 3 ) fprintf(diag, "%5d UNPACK VARIABLE [%06X %05X] starting at %d using table at %d -- %06X %05X=%d\n", sc-1, store[nnn+1], store[nnn], nnn, lsa, store[lsa+1], store[lsa], store[lsa]&0xFFFF); m = 0; w = nnn; while ( m < 100 ) // copy a likely slab of data into a convenient form { lsd = store[w++]; msd = store[w++]; buff[m++] = (msd >>12) & 0xFF; // top char = Q10 and Q9 buff[m++] = (msd >>4) & 0xFF; // next char = Q8 and Q7 buff[m++] = ((msd <<4) & 0xFF) | ((lsd >>16) & 0xF); // next char = Q6 and Q5 buff[m++] = (lsd >>8) & 0xFF; // next char = Q4 and Q3 buff[m++] = lsd & 0xFF; // last char = Q2 and Q1 } buff[m++] = BLOCKEND; // crude protection against running off the end buff[m++] = LINEEND; buff[m++] = BLOCKEND; // crude protection against running off the end buff[m++] = LINEEND; if ( verbosity >= 3 ) fprintf(diag, " buff = %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X - %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X - %02X %02X %02X %02X %02X %02X %02X %02X %02X %02X\n", buff[0], buff[1], buff[2], buff[3], buff[4], buff[5], buff[6], buff[7], buff[8], buff[9], buff[10], buff[11], buff[12], buff[13], buff[14], buff[15], buff[16], buff[17], buff[18], buff[19], buff[20], buff[21], buff[22], buff[23], buff[24], buff[25], buff[26], buff[27], buff[28], buff[29]); if ( verbosity >= 3 ) fprintf(diag, " buff = %c %c %c %c %c %c %c %c %c %c - %c %c %c %c %c %c %c %c %c %c - %c %c %c %c %c %c %c %c %c %c\n", alpha[buff[0]], alpha[buff[1]], alpha[buff[2]], alpha[buff[3]], alpha[buff[4]], alpha[buff[5]], alpha[buff[6]], alpha[buff[7]], alpha[buff[8]], alpha[buff[9]], alpha[buff[10]], alpha[buff[11]], alpha[buff[12]], alpha[buff[13]], alpha[buff[14]], alpha[buff[15]], alpha[buff[16]], alpha[buff[17]], alpha[buff[18]], alpha[buff[19]], alpha[buff[20]], alpha[buff[21]], alpha[buff[22]], alpha[buff[23]], alpha[buff[24]], alpha[buff[25]], alpha[buff[26]], alpha[buff[27]], alpha[buff[28]], alpha[buff[29]]); m = 0; // pointer along buff when reading data d = lsa; // pointer along table entries // Outer loop steps through the successive table entries msd = 0; // ensure first iteration - normally last table entry processed while ( (msd&0x100000) == 0 ) // look for sign digit in last table entry { lsd = store[d++]; // pick up table entry msd = store[d++]; w = 0; // teminator char of last conversion i = 0; // sign of result // Inner loop processes entry(ies) covered by a single table entry while ( w != NUMEND && w != LINEEND && w != BLOCKEND ) { if ( verbosity >= 3 ) fprintf(diag, " Hope to unpack into %d - %06X %05X\n", lsd&0xFFFF, msd, lsd); lsa = msa = 0; // clear out A for accumulation of result - cleared each time i = 0; // sign of result - cleared each time on Ken's advice if ( (msd&0x10000) != 0 ) // alpha data { f = 5; // max number of characters - no of spaces remaining while ( (w = buff[m++]) != NUMEND && w != LINEEND && w != BLOCKEND && --f >= 0 ) { msa = ((msa << 8) | (lsa >> 12)) & 0xFFFFF; lsa = ((lsa << 8) | w) & 0xFFFFF; } a = f; while ( --a >= 0 ) // loop if less than 5 chars read { msa = ((msa << 8) | (lsa >> 12)) & 0xFFFFF; lsa = (lsa << 8) & 0xFFFFF; } } else {//f = 5 + 5 * ((msd&0x20000) >> 17); // max number of characters = 5 or 10 f = 10; // NO, think it is always 10 while ( (w = buff[m++]) != NUMEND && w != LINEEND && w != BLOCKEND && --f >= 0 ) if ( (w&0x7F) == 0x10 ) // -ve sign { i = 0x100000; f ++; // don't count this as a char } else { msa = ((msa << 4) | (lsa >> 16)) & 0xFFFFF; lsa = ((lsa << 4) | (w&0xF)) & 0xFFFFF; } if ( (w&0x7F) == 0x10 ) // -ve sign at the end of 10 digits { i = 0x100000; f ++; // don't count this as a char w = buff[m++]; // do we need to check for NUMEND etc? } } if ( (msd&0x20000) != 0 ) // long { store[a = lsd&0xFFFF] = lsa; store[a+1] = msa | i; // put in the sign digit if ( verbosity >= 3 ) fprintf(diag, " Stored %06X %05X into %d - terminated by %02X %02X f = %d\n", store[a+1], lsa, a, buff[m-2], w, f); } else { store[a = lsd&0xFFFF] = lsa | i; // put in the sign digit if ( verbosity >= 3 ) fprintf(diag, " Stored %06X into %d - terminated by %02X %02X f = %d\n", store[a], a, buff[m-2], w, f); } if ( f < 0 ) // big number needs multiple store instructions { m --; // need to backstep m lsd += 2; // move on to next word - manual is ambiguous here // if ( (msd&0x20000) == 0 ) // doubt the manual here, let's see if it actually happens and it does // notImplemented("UNPACK VARIABLE FIELD DATA more than 10 quartets for short numeric"); } else if ( (msd&0x80000) != 0 && w == NUMEND ) // testing B40 for consecutive items { lsd ++; if ( (msd&0x20000) != 0 ) // long lsd ++; w = 0; // keep the loop going } else if ( w == LINEEND || w == BLOCKEND ) // terminate if end of record msd = 0x100000; // simulate last entry bit // fprintf(diag, "!!! msd = %06X\n", msd); } } msa = msb = lsb = 0; lsa = (m/5)*2 + nnn; // this may need to be changed as ... break; // ... it may be that m is increased in the wrong place /* --------------- */ case 0340: // 28/0/0 - BULK COPY If bit 38 of A = 0 copy short numeric // SHORT NUMERIC words to N onwards the number of // items specified by table entry (A). // 0340: // 28/0/0 - BULK CLEAR SHORT If bit 38 of A = 1 clear short // compartments starting at N. w = msa & 0x1FFF; // bottom 13 bits lsa &= 0x7FFF; // bottom 15 bits if ( (msa&0x20000) == 0 ) // BULK COPY SHORT NUMERIC { for ( i = 0; i= 0 ) // does it matter that we do this backwards - it did for copying store[nnn+w] = 0; } lsa = msa = lsb = msb = 0; break; /* --------------- */ case 0341: // 28/0/1 - BULK COPY SHORT NUMERIC If bit 38 of A = 0 copy short numeric // TO ALPHA words into alpha form to N onwards the // number of items specified by table // entry (A). N must be D + 1 where D is // the first long destination. if ( nnn >= 32768 ) // diff memory reference illegal("Bad memory address in BULK COPY", nnn); if ( (msa&0x20000) != 0 ) // BULK CLEAR flag set - not defined in the manual // notImplemented("BULK CLEAR SHORT NUMERIC TO ALPHA"); { w = msa & 0xFFF; // bottom 12 bits = count of short words while ( --w >= 0 ) // same as for 28/0/0 -- see line 16616 in MR generator store[nnn+w] = 0; } else { msa &= 0xFFF; // bottom 12 bits = count of short words lsa &= 0x7FFF; // bottom 15 bits = source location if ( (nnn&1) == 0 ) // test for valid odd address illegal("Bad address for BULK COPY NUMERIC TO ALPHA", nnn); w = -1; while ( ++w < msa ) // copy one word at a time { lsd = store[lsa+w]; msd = lsd & 0x100000; // expand to 41 bits lsd &= 0xFFFFF; storeAlpha(nnn + w + w); } } lsa = msa = lsb = msb = 0; break; /* --------------- */ case 0344: // 28/1/0 - BULK COPY ALPHA TO SHORT If bit 38 of A = 0 copy alpha words to // NUMERIC short numeric form words to N onwards // the number of items specified by table // entry (A). if ( (msa&0x20000) != 0 ) // BULK CLEAR flag set - not defined in the manual notImplemented("BULK CLEAR ALPHA TO SHORT NUMERIC"); msa &= 0xFFF; // bottom 12 bits = count of short words lsa &= 0x7FFF; // bottom 15 bits = source location if ( (lsa&1) == 0 ) // test for valid odd address illegal("Bad address for BULK COPY ALPHA TO NUMERIC", lsa); if ( verbosity >= 8 ) { fetchAlpha(lsa); fprintf(diag, "Copied %06X %05X [%s] => %06X ", store[lsa], store[lsa-1], tostr(store[lsa], store[lsa-1]), lsd | msd); fetchAlpha(lsa+2); fprintf(diag, "%06X %05X [%s] => %06X\n", store[lsa+2], store[lsa+1], tostr(store[lsa+2], store[lsa+1]), lsd | msd); } w = -1; msa = msa/2; while ( ++w < msa ) // copy one word at a time { fetchAlpha(lsa + w + w); store[nnn+w] = lsd | msd; // OR puts in the sign digit } lsa = msa = lsb = msb = 0; break; /* --------------- */ case 0345: // 28/1/1 - BULK COPY LONG NUMERIC If bit 38 of A = 0 copy long numeric or // OR ALPHA alpha words to N onwards the number // of items specified by table entry (A). // 0345: // 28/1/1 - BULK CLEAR LONG If bit 38 of A = 1 clear long compartment w = msa & 0x1FFF; // bottom 13 bits if ( (msa&0x20000) != 0 ) // BULK CLEAR LONG { while ( --w >= 0 ) // cannot see why clear long is different from clear short store[nnn+w] = 0; // does it matter that we do this backwards - might do for copying } else { for ( i = 0; i= 3 ) fprintf(diag, "%5d EDIT into %d using table at %d\n", sc-1, nnn, lsa); msd = 0; // ensure entry to loop m = 0; // pointer along buffer while ( (msd & 0x100000) == 0 ) // multiple entries if sign not set { int src; // source int nchs; // number of chars int nsps; // number of spaces int ndsc; // number of chars to discard int nnsz; // number of non-sig zeros to replace src = (lsd = store[lsa++]) & 0xFFFF; // source nchs = (msd = store[lsa++]) & 0xF; // Q6 = number of chars nsps = (msd>>4) & 0xF; // Q7 = number of spaces ndsc = (msd>>8) & 0xF; // Q8 = number of chars to discard nnsz = (msd>>12) & 0xF; // Q9 = number of non-sig zeros to replace if ( verbosity >= 3 ) { fprintf(diag, "Chars generated so far = %d\n", m); fprintf(diag, "src %d (sh = %d) -- ", src, lsd>>16); fprintf(diag, "nch = %d nsp = %d nds = %d nz = %d end = %02X\n", nchs, nsps, ndsc, nnsz, msd>>16); fprintf(diag, "Table entry points at %06X and N points at %06X %05X\n", store[src], store[nnn+1], store[nnn]); } while ( nsps-- > 0 ) // insert spaces buff[m++] = 0; if ( (msd&0x10000) != 0 ) // long alpha { lsb = store[src++]; msb = store[src] & 0xFFFFF; // ignore sign digit if ( verbosity >= 3 ) fprintf(diag, "Editing %06X %05X alpha\n", msb, lsb); while ( ndsc-- > 0 ) // discard { msb = ((msb<<8) | (lsb>>12)) & 0xFFFFF; lsb = (lsb<<8) & 0xFFFFF; } while ( nchs-- > 0 ) // copy digits, converting to sextet { w = (msb>>12) & 0x3F; // just what is an alpha sextet? if ( (w&0xF) != 0 ) // seems we need 0x40 bit except for sp - & 0 w |= 0x40; // so let us put it in -- most odd see 16.3.2 Vol I if ( w != 0x30 ) nnsz = -1; // stop zero suppression else if ( --nnsz >= 0 ) w = 0; buff[m++] = w; msb = ((msb<<8) | (lsb>>12)) & 0xFFFFF; lsb = (lsb<<8) & 0xFFFFF; } } else // numeric { if ( (msd&0x20000) != 0 ) // long numeric lsb = store[src++]; msb = store[src]; // either short number or top of long one lsb &= 0xFFFFF; // discard any spurious sign bit f = (msb>>16) & 0x10; // space or minus for later use d = 0; // digit for mode 1 if ( (msd&0x40000) == 0 ) // B39 indicates sign digit required f = -1; // no sign digit at the end else if ( (msd&0x80000) != 0 ) // B40 indicates sign digit in card format { d = f; // digit for mode 1 f = -1; // no sign digit on the end } if ( verbosity >= 3 ) fprintf(diag, "Editing %06X %05X numeric (sign = %02X %02X)\n", msb, lsb, f&0xFF, d); msb &= 0xFFFFF; // discard sign bit while ( ndsc-- > 0 ) // discard { msb = ((msb<<4) | (lsb>>16)) & 0xFFFFF; lsb = (lsb<<4) & 0xFFFFF; } if ( nchs == 0 ) nchs = 16; while ( nchs-- > 0 ) // copy digits, converting to sextet { w = msb>>16; if ( w != 0 ) { nnsz = -1; // stop zero suppression w += d; // set bit in most sig digit d = 0; // prevent setting bit in later digits } else if ( --nnsz < 0 ) // w is already a space char w = 0x30; // we need a real zero char buff[m++] = w; msb = ((msb<<4) | (lsb>>16)) & 0xFFFFF; lsb = (lsb<<4) & 0xFFFFF; } if ( f >= 0 ) // terminating sign digit required buff[m++] = f; // Leo III minus or space stuck on the end } } if ( verbosity >= 3 ) fprintf(diag, "Chars generated so far = %d\n", m); buff[m] = 0; buff[m+1] = 0; buff[m+2] = 0; buff[m+3] = 0; // spurious spaces in case m%5 != 0 i = 0; w = 0; // now to store away the result while ( m > 0 ) { store[nnn++] = ((buff[w+2]<<16) | (buff[w+3]<<8) | buff[w+4]) & 0xFFFFF; store[nnn++] = (buff[w]<<12) | (buff[w+1]<<4) | (buff[w+2]>>4); m -= 5; w += 5; if ( verbosity >= 3 ) fprintf(diag, " Stored %06X %05X at address %d - %s\n", store[nnn-1], store[nnn-2], nnn-2, tostr(store[nnn-1], store[nnn-2])); } if ( m != 0 ) fprintf(diag, "%6d Spurious chars end of edit = %d\n", sc-1, -m); msa = lsa = msb = lsb = 0; break; /* --------------- */ case 0347: // 28/1/3 - CONDENSE Condense items into variable field // layout in alpha sextet form in // compartments starting at N according // to table entry starting at (A). // notImplemented("CONDENSE"); // new implementation verbatim from sect 16.3.2 in Vol I store[1] = sc; store[32] = creg; // (i) step number in manual creg = lsa; // (ii) m = 0; // buffer pointer, not yet using A properly msd = 0; while ( (msd&0x100000) == 0 ) // last bit not set in previous entry { lsd = store[creg++]; msd = store[creg++]; //(iii) & (vii) & (viii) if ( verbosity >= 3 ) fprintf(diag, "D reg set to %06X %05X [%d %d]\n", msd, lsd, msd&0xFFFF, lsd); while ( (msd&0xFFFF) >= lsd ) // note (iv) { lsb = store[lsd++]; // (iv) begin if ( (msd&0x20000) == 0 ) // short // (v) msb = 0; // using B as workspace else msb = store[lsd++]; // using B as workspace if ( (msd&0x10000) == 0 ) // numeric { notImplemented("CONDENSE numeric"); } else // alpha { if ( msb != 0 || lsb != 0 ) { buff[m++] = msb >> 12; buff[m++] = (msb>>4) & 0xFF; // or should we be masking with 0x7F?; buff[m++] = ((msb<<4) | (lsb>>16)) & 0xFF; buff[m++] = (lsb>>8) & 0xFF; buff[m++] = lsb & 0xFF; } buff[m++] = NUMEND; } // (iv) end } // (vi) end } // (viii) sc = store[1]; creg = 0; // (x) // now shift the result into store -- really should do this properly via A buff[m] = 0; buff[m+1] = 0; buff[m+2] = 0; buff[m+3] = 0; // spurious spaces in case m%5 != 0 ??? i = 0; w = 0; // now to store away the result while ( m > 0 ) { store[nnn++] = ((buff[w+2]<<16) | (buff[w+3]<<8) | buff[w+4]) & 0xFFFFF; store[nnn++] = (buff[w]<<12) | (buff[w+1]<<4) | (buff[w+2]>>4); m -= 5; w += 5; if ( verbosity >= 3 ) fprintf(diag, " Stored %06X %05X at address %d - %s\n", store[nnn-1], store[nnn-2], nnn-2, tostr(store[nnn-1], store[nnn-2])); } creg = msa = lsb = msb = 0; lsa = nnn; // (x) // notImplemented("CONDENSE"); // new implementation verbatim from sect 16.3.2 in Vol I break; /* --------------- */ case 0350: // 29/0/0 - EDIT FOR HOLLERITH OUTPUT After action 28/1/2 edit up to 80 // characters for card punch output by // General Purpose Output Assembler // into N onwards. notImplemented("EDIT FOR HOLLERITH OUTPUT"); break; /* --------------- */ case 0351: // 29/0/1 - EDIT FOR ANELEX OUTPUT After action 28/1/2 edit up to 160 // characters for line printer output by // General Purpose Output Assembler // into N onwards. if ( verbosity >= 3 ) { fprintf(diag, " Data to be edited: %06X %05X - %06X %05X - %06X %05X - %06X %05X - %06X %05X\n", store[lsa+1], store[lsa], store[lsa+3], store[lsa+2], store[lsa+5], store[lsa+4], store[lsa+7], store[lsa+6], store[lsa+9], store[lsa+8]); for ( i = 0; i<64; i+=2 ) // print all 160 characters fprintf(diag, "%s", tostr(store[lsa+i+3], store[lsa+i+2])); fprintf(diag, "\n"); } for ( i = 0; i<66; i++ ) // copy control word and all 160 characters to transit area store[nnn+i] = store[lsa+i]; // printer MR routine processes these // This implementation does not emulate the proper interface // The data is merely copied to the printer buffer, from where it is printed by the MR emulation // Will need a serious rethink to work with a proper master routine break; /* --------------- */ case 0361: case 0362: case 0363: m = mval; case 0360: // 30/0/m - TRANSFER DOUBLE LENGTH Transfer (AB) to N + 2 and N and clear // A and B. pp = (nnn + m) & 077777; // memory address notImplemented("TRANSFER DOUBLE LENGTH"); break; /* --------------- */ case 0365: case 0366: case 0367: m = mval; case 0364: // 30/1/m - COPY DOUBLE LENGTH Copy (AB) to N + 2 and N. pp = (nnn + m) & 077777; // memory address notImplemented("COPY DOUBLE LENGTH"); break; /* --------------- */ case 0371: case 0372: case 0373: m = mval; case 0370: // 31/0/m - ADD DOUBLE LENGTH Add (N + 2) and (N) to (AB). pp = (nnn + m) & 077777; // memory address notImplemented("ADD DOUBLE LENGTH"); break; /* --------------- */ case 0375: case 0376: case 0377: m = mval; case 0374: // 31/1/m - SUBTRACT DOUBLE LENGTH Subtract (N + 2) and (N) from (AB). // * Magnetic tape only. pp = (nnn + m) & 077777; // memory address notImplemented("SUBTRACT DOUBLE LENGTH"); break; /* --------------- */ default: interrupt(16); // illegal("Unrecognised action", f); } } } void loadprog(char *fn) { FILE *fin = fopen(fn, "r"); int sc = startload; // start loading code here int xloc = -1; // location of extra chapter if ( fin == NULL ) { perror(fn); illegal("Cannot open binary program", 0); } fgets(buff, 999, fin); if ( buff[0] == '<' ) // skipping start of listing file { if ( verbosity >= 2) fprintf(diag, "Skipping to start of binary program\n"); while ( memcmp(fgets(buff, 999, fin), "=============== Second pass", 26) != 0 ) ; fprintf(diag, "Loading program from file %s\n\n", fn); } else { fprintf(diag, "Loading program from file %s\n%s\n", fn, (char *)buff); if ( memcmp(buff, " PASS3 marker", 16) == 0 ) prognum = strdup(buff+29); // copy programme number for use in mag tape headers } while ( fgets(buff, 999, fin) != NULL ) if ( strlen(buff) > 5 && *buff != ' ' ) { int f = atoi(buff); int n = f<<3; int i = 4; int a = 0; int m, d, *ovr; if ( f == 0 && *buff != '0' ) // directive { if ( *buff == 'L' ) // change loading address { sc = atoi(buff+1); if ( verbosity >= 2) fprintf(diag, "Load address %s\n", (char *)buff+1); } else if ( *buff == 'E' ) // specify entry point startexec = atoi(buff+1); else if ( *buff == 'T' ) // specify tag value for subsequent loading tagreg = atoi(buff+1); else if ( *buff == 'X' ) // specify location of extra chapter store[3] = // used by PTS to address extra chapter xloc = atoi(buff+1); // used by emulator to access device type info else if ( *buff == 'O' ) // create overlay { i = atoi(buff+1); // overlay number a = atoi(buff+4); // overlay low address n = atoi(buff+10) - a; // overlay size ovr = (int *)malloc(d = n * sizeof(int)); overlay[i] = ovr; // buffer area for overlay memcpy(ovr, store + a, d); // preserve overlay for later use if ( verbosity > 0) fprintf(diag, "Overlay %d from %d to %d\n", i, a, a+n-1); oladdr[i] = a; olsize[i] = d; } } else { while ( buff[++i] > ' ' ) ; m = atoi(buff+i-1); d = atoi(buff+i-3); n += d*4 + m; if ( buff[i] == ' ' && buff[++i] != ' ' ) // if there is an address part a = atoi(buff+i); if ( quiet == 0 ) fprintf(diag, "%4d %2d/%d/%d %4d| %s", sc, f, d, m, a, (char *)buff); tag[i] = tagreg; store[sc++] = (n<<13) + a; if ( m >= 4 || d >= 2 || f >= 32 || a >= 8192 ) { fprintf(diag, "Error: %s", (char *)buff); exit(1); } } } fclose(fin); if ( xloc >= 0 ) { int i; int w, t, a; fprintf(diag, "Extra chapter at %d\n", xloc); if ( (w = store[xloc+1]) == 0 ) // no modifier group needed fprintf(diag, " No modifier group required\n"); else { fprintf(diag, " Modifier group set to %d\n", w); groupref = w * 8; } topcreg = creg = 0x66666; // set decimal radix for normal user programmes i = -1; while ( store[a = 2*++i + xloc + 35] != 0 ) // this is an entry for a genuine device { w = store[a-1]; t = w>>16; // type number w &= 255; // route number dvfn[w] = store[a]; buff[0] = 0; if ( t != dvty[w] ) { sprintf(buff, " type mismatch - route was type %d", dvty[w]); dvty[w] = t; // we need it to match the programme being run } fprintf(diag, " File %d is %c%c type %d on route %02o%s\n", i, alpha[(dvfn[w]>>8)&127], alpha[dvfn[w]&127], dvty[w], w, (char *)buff); } } else printf("N.B. Program without chapter 0\n"); fprintf(diag, "Program %s loaded\n\n", fn); if ( overlay[0] != NULL ) // overlaid program memcpy(store + oladdr[0], overlay[0], olsize[0]); // need to reinstate overlay 0 } void loadleo(char *s) // load either a Leo mag tape start or paper tape start { if ( s[0] == 'M' ) // mag tape start { sprintf(buff, "m00 %s\n", s); opscometc(); store[64] = 0; // annex for route 0/0 store[1000] = 0x132000; // 19/0/1 read MT store[1001] = 0x188000; // 24/1/0 jump to word 0 startexec = 1000; printf("Cannot do MT start yet"); exit(1); } else // paper tape start - probably { printf("Cannot do PT start yet"); exit(1); } } void readconfig(int *map, int marker) // read peripheral configuration from routes.txt // types are stored in map with the value of marker added to each // values for unspecified routes are set to -1 { int i, j, t, r; FILE *fin = fopen("routes.txt", "r"); for ( i = 0; i<64; i++ ) map[i] = -1; if ( fin == NULL ) // look in parent directory fin = fopen("../routes.txt", "r"); if ( fin == NULL ) printf("No routes file found\n"); else { while ( fgets(buff, 250, fin) != NULL && memcmp(buff, "=======", 5) != 0 ) { i = atoi(buff); j = atoi(buff+2); t = atoi(buff+3); r = i*8 + j; map[r] = t + marker; if ( verbosity > 0 ) fprintf(diag, "Device type %d configured on route %02o/%d = %d\n", t, i, j, r); } } } int main(int argc, char **argv) { int i, j; char *s; struct tm *nowb; time_t now; diag = stdout; mkchars(); // set up character conversion arrays mkalpha(); // including the inverse tagreg = 4; // temp ?? buff[0] = 0; // null string i = 0; lsa = 0; msa = 0; // Leo starts with zero in A by default while ( ++i < argc && *(s = argv[i]) == '-' ) // process switches { strcat(buff, " "); strcat(buff, s); if ( *++s == 'd' ) diag = fopen("log.txt", "w"); // specify diagnostic file else if ( *s == 's' ) // quiet - omit listing of program read in quiet = 0; else if ( *s == 'v' ) // set verbosity verbosity = atoi(s+1); else if ( *s == 't' ) // turn-on address, set verbosity = 9 at this instruction count turnOn = atoi(s+1); else if ( *s == 'w' ) // turn-off diagnostics during a subroutine at this address turnOff = atoi(s+1); else if ( *s == 'm' ) // monitor address, print message whenever its value changes monloc = atoi(s+1); else if ( *s == 'n' ) // monitor address, print message whenever its value changes monnum = atoi(s+1); else if ( *s == 'a' ) // abandon execution after this many instructions abandon = atoi(s+1); else if ( *s == 'f' ) // abandon execution 1000 after first passing this point, put -v9 faband = atoi(s+1); else if ( *s == 'e' ) // echo console input -- -eh gives HTML colouring if ( s[1] == 0 ) echofmt = "%s\n"; else if ( s[1] == 'h' ) // echoing in HTML { printf("Printer\n
\n");
            echofmt = "%s\n";
            redfmt = "%5d T/W: %s\n";
            blkfmt = "%5d T/W: %s\n";
         }
         else                            // assume VT100
            echofmt = "\x1B[32;1m%s\x1B[0m\n";
      else if  ( *s == 'V' )             // VT100 colour escape sequences for typewriter output
      {  redfmt = "%5d T/W: \x1B[31;47;1m%s\x1B[0m\n";
         blkfmt = "%5d T/W: \x1B[30;47;1m%s\x1B[0m\n";
         cntfmt = "\n\x1B[0m         : \x1B[30;47;1m";
         rhpad = strlen(cntfmt) + 37;
      }                                  // ANSI escape sequence for black text on a white background
      else if  ( *s == 'o' )             // operators console app on this address
         opscons = s+1;
      else if  ( *s == 'p' )             // printer app on this address
         netprint = s+1;
      else if  ( *s == 'T' )             // initial value of tagreg
         tagreg = atoi(s+1);
      else if  ( *s == 'b' )             // no more -- suppress buffer swapping in master routine
         maxblox = atoi(s+1);            // define largest allowed mag tape block
      else if  ( *s == 'A' )             // put a value in A
      {  readregval(s+1);                // reads an initial register value into D
         lsa = lsd;
         msa = msd;
      }
      else if  ( *s == 'B' )             // put a value in B
      {  readregval(s+1);                // reads an initial register value into D
         lsb = lsd;
         msb = msd;
      }
      else if  ( *s == 'C' )             // put a value in C
      {  readregval(s+1);                // reads an initial register value into D
         creg = lsd;
         topcreg = creg & 0xF0000;       // selected "radix" of most sig digit
         topcreg = topcreg | (topcreg>>4) | (topcreg>>8) | (topcreg>>12) | (topcreg>>16);
      }
      else if  ( *s == 'i' )             // set value of auto flag (NZ for autonomous peripherals)
         ioauto = atoi(s+1);
      else if  ( *s == 'c' )             // turn on millisecond timer
         mstime = 1;
      else if  ( *s == 'I' )             // put a value in the indicator register
         indreg = atoi(s+1);
   }
   if  ( monnum >= 21 )
      monnum = 20;
   monval[0] = -1;                       // force a report on first change, even if zero

   for  ( j = 0; j<32768; j++ )         // clear store
   {  tag[j] = tagreg;                  // set all tags to current tag 08004 seems to need 1
      store[j] = 0;
   }

   if  ( diag == NULL )
   {  perror("log.txt");
      illegal("Cannot open log file", 0);
   }

   if  ( i >= argc )
   {  fprintf(stderr, "Usage %s {switches} binary_prog [ptr_input]\n", *argv);
      fprintf(stderr, "      Look at: http://sw.ccs.bcs.org/leo/leo3.c\n", *argv);
      exit(1);
   }

   fprintf(diag, "Switches are: %s\n", (char *)buff);

   readconfig(dvty, 0);       // read the peripheral configuration
   if  ( strcmp(s + strlen(s) - 4, ".leo") == 0 )          // Leo III start MT or paper tape
      loadleo(s);
   else
      loadprog(s);
   if  ( ++i < argc )         // there is another parameter
      ptr_fn = argv[i];       // which is the name of the paper tape input

   store[138] = 0x90312;      // 9 March 2012
   time(&now);                // sort out date
   nowb = localtime(&now);
   i = nowb->tm_mon + 1;
   datereg = i;
   nowleo = (i/10) << 12;
   nowleo += (i%10) << 8;
   i = nowb->tm_year % 100;
   nowleo += (i/10) << 4;
   nowleo += i%10;
   i = nowb->tm_mday;
   datereg += (i%10) << 4;
   nowleo += (i%10) << 16;
   store[138] = nowleo;
   datereg += (store[139] = (i/10)) << 8;

   i = nowb->tm_hour;
   nowleo = ((i/10)*6 + i) << 12;
   i = nowb->tm_min;
   nowleo += ((i/10)*6 + i) << 4;
   store[156] = nowleo;

   memset(g4mark, 0, sizeof(g4mark));

   if  ( opscons != NULL
        &&  (opsock = get_connected(opscons, 8888)) < 0 )
      perror("Ops console");

   if  ( netprint != NULL
        &&  (lpsock = get_connected(netprint, 8889)) < 0 )
      perror("Network printer");

   indreg |= groupref<<10;
   showIndicators(0);

   interpret(startexec);
   iosum();
   return 0;
}

int get_connected(char *target_thost, unsigned short target_tport)
{  int conskt = -1;
   int n;
   struct sockaddr_in target_end_addr;
   struct in_addr target_address;

   struct hostent *he;

#ifdef WIN32
   WORD wVersionRequested;
   WSADATA wsaData;
   int err;

   wVersionRequested = MAKEWORD(2, 1);

   err = WSAStartup(wVersionRequested, &wsaData);
   if ( err != 0 )
   {   fprintf(stderr,"WSAStartup failed with error: %d\n", err);
	   return err;
   }
#endif

   if  ( *target_thost == 0 )      // null name so use localhost
      target_thost = "127.0.0.1";
   he = gethostbyname(target_thost);
   memcpy(&target_address.s_addr, *(he->h_addr_list), sizeof(target_address.s_addr));
   
   memset((char *) &target_end_addr, 0, sizeof(target_end_addr));
   target_end_addr.sin_family      = PF_INET;
   target_end_addr.sin_addr        = target_address;   /* .s_addr */
   target_end_addr.sin_port        = htons(target_tport);
   
   if  ( verbosity >= 2 )
      printf("Creating Socket\n");

   conskt = socket(AF_INET, SOCK_STREAM, 0);
   if  (conskt < 0)
   {  fprintf(stdout, "Socket failed. errno = %d.\n", errno);
      fprintf(stdout, "No direct connect tcp socket\n");
      perror("socket creation");
      return -1;
   }
   
   if  ( verbosity >= 2 )
      printf("Socket %d\n", conskt);

/* if  ( set_options(conskt) < 0 )
   {  fprintf(stdout, "can't set socket option(s)\n");
      return (-1);
   }
 */
   if  (connect( conskt, (struct sockaddr *)&target_end_addr, sizeof(target_end_addr)) < 0)
   {  fprintf(stdout, "Connect failed. errno = %d.\n", errno);
      perror("socket connection");
   }

   if  ( verbosity >= 2 )
      printf("Connection complete\n");

   n = sizeof(target_end_addr);
   if  ( getsockname(conskt, (struct sockaddr *)&target_end_addr, &n) < 0 )
      perror("getsockname");
   else if  ( verbosity >= 2 )
      printf("IP num at this end: %s\n", inet_ntoa(target_end_addr.sin_addr));

   return (conskt);
}