1. Corporate
The System 10 was first built by Friden, a flexowriter company, that entered into the point of sale business and was then acquired by Singer as part of a diversification. Singer eventually sold off its divisions to focus on its core business, and the design and development rights were sold to ICL, and the customer base to TRW.
2. Opcodes
In your disassembly of the instruction, you have it mostly right. The AS and BS bits actually specify a common address as opposed to a partition, common addresses are always negative. The "Y" bits were used for indirect and extended addressing in the model 21 processor only, for backwards compatibility they were negative true. The first bit indicated that A was indirect, followed by 80 40 20 10 in BCD for extended common addresses, the remaining five bits were the same for the "B" operand. If the extended address was used and the sign bit was off, the program would go load/local (no extended partition addressing), until the model 25 came out much later.
3. Pipelining
You mentioned that the processor had to fetch all 10 bytes to execute an instruction; this is actually incorrect. At first it would only fetch 4 bytes which was enough to fetch the opcode and the LA and A operand. In some instructions the 5th byte was ignored, especially in branch instructions, because all instructions are on a 10"s boundary. The rest of the instruction would be pulled only if it needed to be, hence a successful branch on the LA condition would ignore the rest of the instruction. We used to use this to pack constants and other data into unused memory when it got pretty tight. (unused branch instructions were great for disk addresses).
4. Operations
I/O: Opcode 0000 was a read, and 0001 was a write. The LA specified the device number, the LB operand specified the channel and mode. There were two channels, the file access channel (FAC) was channel 0, and the partition was channel 1. Hence a read into location 1000 for a length of 10 from device 0 (usually CRT) would be 0100010010, and the corresponding write would be 010P010010.
There were actually 3 modes of I/O instruction, normal, control and no fill. In a normal read instruction the processor would read characters from the device and fill the field with blanks to the length specified in the instruction, it would always write the full length and a write no fill did the same as the normal write. If you look at the mapping of the internal six bit character to the ASCII character set, you will find that there is one bit missing: Bit 6. The characters actually mapped to bits 1, 2, 3, 4, 5, and 7. The purpose of the write and write control instructions was to specify the value of bit 6. This saved about 15% of core memory which, at the time, was very expensive. Write normal ADDS bit 6, write control sets it to zero. Conversely a read maps the unfilled locations to blanks, and a read control (load/local) sets them to zeros. This is how the bootstrap worked: when the partition went load/local it would be forced to location 9990 and execute a read control of length 10 into location 0, which it would then execute. if you decode an instruction of 0000000000, it is a read from FAC device 0 into partition from the address specified in location 0000 (LB operand). Hence you get sector 0 of drive 0. I used to use these instructions to patch disk sectors by hand.
There was another boot in DMF II, E SYS would jump to location 390 in common where a device-independent bootstrap would reside.
Instructions
The ADD instruction was a two-length, unlike the Move Numeric and Move Character, which was one.
You would add a field length LA to a field length LB, likewise with subtract.
Multiply was weird: you would multiply number A length LA by number B length LB to get a result at B length LA+LB.
The divide was even weirder: you divide A length LA into B length LA+LB, place the quotient at A with LA and remainder at B with LB.
Form numeric was a two-length instruction, so was edit.
Branch condition 8 was branch and switch, which caused a partition switch, and 9 was never (same as zero).
5. Code example
In your code example you used both branches of the instruction to go high or low. The assemble actually had a BNE instruction, it would create a branch if equal to the next instruction, else unconditionally somewhere else.
Normally this would be used, however in my mutex example, it is important that no branch be taken when the mutex is seized. Hence the rather odd coding style.
As noted above, branch condition 8 would switch partitions, so should be part of the loop when the mutex is not seized.
6. LIOCS
Ah, LIOCS! How we did battle together many a day into the small hours of the next day (and sometimes the day after). For a machine with such limited appeal, this was really ahead of its time. Singer must have borrowed some top-notch designers who put this together. The first amazing thing about it was the macro itself. This machine had one of the most complex, full-featured and rocket science macro assemblers that I have ever seen in my entire career, which spans over 67 assembler languages. If only I could find the manual today!
There were actually 5 types of files that I can remember, (not including terminals),
- linked sequential,
- doubly linked sequential,
- indexed link sequential,
- relative access and
- direct access.
The first is straightforward, it was used mostly for batch processing of data that was used once.
Doubly linked sequential were source files, as you point out.
ILS files were the beginnings of a data base, the only problem was that the index was not updated on the fly; if you added records to it you would have to consistently rebuild the index file using the MAINT program, which could take hours.
Relative access gave you the full 100 character sector and was contiguous disk space, records could span multiple sectors up to a maximum of 1000 characters.
Direct access used a key composed of up to 49 alphanumeric or 99 numeric characters; it ran it through a hashing algorithm (called _PRIME), until it had something less than the file size. If that sector was empty, it would write it there. (basically a random access version of direct access). Later in my career I wrote a system on a DEC PDP-11 that was used to replace a system 10, I could really have used some of the disk access methods..
7. Terminals
There were 5 types of partitions that I could remember;
- The MTIOC (multi-terminal IO channel),
- The MDIOC (multi data terminal IO channel),
- the ACA (asynchronous communications adapter),
- SCA (synchronous communications adapter) and
- ATA (asynchronous terminal adapter).
The MDIOC was designed for Friden/Singer point of sale terminals. Using the MDIOC, the entire machine could run up to 190 cash registers (one MTIOC partition), with very little memory. The retail software would all run in common, so they actually ran with 0K in partition!.
Cover of a Singer-10 ATA manual
The ACA and SCA were very close, and both blind partitions. Mapping control characters was really weird: - in both cases. Say for example you wanted to send the simple string STX (data) ETX and wait for a response. The device number in the write instruction would specify how many leading characters were sent as control characters; eg you would put a B on the front of the data and write to device 1. The read instruction doubled as a write and a read, to get the ETX out you would place it at the beginning of the read buffer and do a read to device 1 again. The hardware would then temporarily turn the read into a write for the length of A, insert an automatically generated checksum, and then start reading. The ATA was more sane: it was a normal partition that would interface to regular devices, but it would only go 110 or 300 bits/sec.