Wang 2200 Disk Channel Description
Although Wang was relatively new to producing computers and the 2200 wasn't in the same class as the "big iron" computers, Wang took an admirably long view of how to address its disk drives. Rather than having the CPU microcode directly controlling the disk drive stepper motor and decoding the data, Wang established a protocol that abstracted away the details of the attached disk. This allowed connecting a wide variety of disk drives to the same 2200 CPU without having to rewrite the 2200 microcode for each new drive. The cost was having to put a smart controller on the other end of the cable to translate the abstract protocol into electrical signals that the drive could understand.
In the diagram below, the card labeled "disk interface" is nothing much more than a 2200 I/O bus interface connected to some line drivers and receivers; the disk interface has no intelligence at all. The cable between the 2200 CPU and the disk drive could be tens of feet to hundreds of feet if repeaters were used.
Ironically, the 2200 CPU has a single 4 bit 74181 ALU unit, whereas the Wang disk controllers used two 74181s to form an 8b microcoded processor. Although the disk controller processor was 8 bits wide, in all other respects it was much less capable than the 2200 CPU.
In order to add support for emulated disk drives to WangEmu, the disk channel protocol had to be reverse engineered. One way would have been to attach a logic analyzer to the disk interface cable, run various controlled scenarios and then analyze the resulting waveforms until the communication protocol was understood. Fortunately, one of the Wang internal documents, Module Repair Guide No. 2 was available. Although it doesn't contain a complete description of the protocol, it does have a fairly good description of the microcoded CPU and supplies the microcode listing from the PCS-II disk controller. That listing has been reconstructed as a text document.
Unfortunately, it appears as if the microcode was entered as hex since the symbolic description doesn't supply all the required information; the symbols appear to just be extended comments. For instance, the first instruction is appears as
0000 FC04 CNTRL-2 SELECT DISK. #1
The first field is the address, the second field is the 16 bit microword, the third field is the symbolic operation, and the fourth field is the comment. One has to know that bits [7:0] of CNTRL-2 is actually an immediate value in order to parse this line. It was hard to decipher the exact meaning of each line, so a disassembler was written to make all the encoded information explicit. The original listing has been augmented with the disassembled information between "[" and "]" in the enhanced listing. Reverse engineering the details of the protocol became much easier with the enhanced listing.
Below is a rough description of the handshake that was worked out and is the basis of the disk channel emulation in WangEmu. But first, a few general properties of the Wang disk model:
- all sectors contain 256 bytes, from the smallest floppy disk to the largest hard disk
- sectors are logically addressed sequentially starting from 0; it is up to the disk controller to map the sequential sector address to a track and sector-of-track address
- the disk controller is responsible for performing any sector interleave computation
- a disk may have at most 65536 sectors because sector addresses are communicated via a 16b number; this puts a limit on a logical disk size of 16 MB. Physically larger disks would have to be partitioned into smaller logical disks, each of which is 16 MB or less. In 1973 that probably seemed reasonable, but before 1980 Wang was already hitting that limitation.
- the communication protocol has a lot of error checking. All command bytes sent from the 2200 CPU to the disk controller get echoed back from the controller to the CPU; if the echoed byte is incorrect, the 2200 can abort the operation before any changes are made to the disk. All data transfers have an LRC byte appended. All operations have one or more points where the disk controller can signal to the 2200 CPU that an error has been detected.
- the protocol doesn't allow for queuing commands at the disk controller (although it might be done at the 2200 CPU side, it wasn't useful to do so until the MVP evolution), nor does it allow seeking to a given track other than by reading a sector on that track.
- a disk channel is attached to at most two drives: drive #1 and drive #2
- data on the disk is protected by a 16 bit CRC checksum, and data transferred across the disk channel is protected by an LRC checksum byte (sum of all 256 bytes mod 256). The CRC generation and checking is hidden from the 2200 other than the fact that a status byte returned from the disk controller can indicate that the sector had a CRC checksum error.
There are four operations supported by the disk controller
Note 2006-10-10:
All of the information on this page was reverse engineered from that single tech note plus a lot of time experimenting with the 2200T emulator. The disk channel protocol was limited to what is described here (also, there is a limit of 32K sectors on a disk, even though the protocol allows specifying 64K sectors).
I knew I was missing something, especially since later models support drives with more than 32K sectors (and later, even more than 64K sectors), plus things like software controlled disk formatting.
Paul Szudzik has come to the rescue and outlined later disk channel developments. This page will be updated at some later point with the details, but in essence: after the command initiation by the host CPU, rather than returning a 0xC0 status byte, the disk controller returns a 0xD0 status byte, indicating it can deal with the new protocol.
Format a Disk
Disks are formatted by pressing a recessed button on the front of the disk unit housing. Any disk in drive #1 will be formatted and verified. While this is going on, an LED blinks on the front panel to let the operator know that the format is in progress. Although it seems inconvenient that the format function isn't accessible from software, it guarantees that an errant program can't accidentally format the disk.
Part of formatting includes writing a header indicating the track and sector address of each sector and writing a zeroed out data payload and LRC byte.
Command Initiation
The three commands that the 2200 CPU can initiate (read, write, verify) begin with a common handshake sequence that will be detailed here so it doesn't need to be repeated for each command description later.
In the sequences listed below, some transfers from the Wang have a (*) after the byte listed, and the comment sometimes mentions "CAX." In the Wang I/O bus protocol, the CPU puts the address of the device it wants to communicate with on the address bus (AB), then it sends an address strobe (ABS) to finally select the device. That selection is maintained until the next ABS strobe. After the ABS strobe, the AB bus can be set to an arbitrary 8b value without affecting the selection. Most devices don't use AB other than during the selection phase, but the disk controller is different. It monitors the AB bus during output data strobes (OBS) to signal whether the 2200 CPU wants to continue the sequence or if it wants to abort and restart. Specifically, if bits 8 and 6 of AB are 1, it indicates that the sequence in progress is to be aborted. In fact, the disk controller doesn't always monitor CAX and only checks it at certain junctures in the sequence.
Table 1 shows the first few transfers of this handshake that is common to the read, write, and verify commands. If the status byte returned by the disk controller in step 9 is not 0x00, then the 2200 CPU and the disk controller stop the sequence in progress and the 2200 must start a new command sequence by sending the CAX reinit request from step 1.
The three command bytes sent from the 2200 to the disk controller are the operation and drive selection byte, the most significant 8b of the sector address, then the least significant 8b of the sector address. As each of these three command bytes are received, the CAX condition is checked and if true, the disk controller aborts the sequence and replies with 0xC0 and continues on with step 3 of the command initiation sequence. This is used, for example, if the 2200 sends a command byte that either the disk controller receives incorrectly or the echo is received by the 2200 incorrectly; not trusting what the disk controller might have received, the 2200 CPU just aborts the command altogether.
Error responses are produced at other junctures of the sequences, and not just step 9. The status bytes that appear to be in use are given in Table 2, below.
| step | 2200 sends | disk sends | comment |
|---|---|---|---|
| 1 | 0x00* | CAX re-init request | |
| 2 | 0xC0 | re-init request acknowledgment | |
| 3 | <command byte 1> | read/write/verify command (cmd1) 0x00: read sector, drive #1 0x10: read sector, drive #2 0x40: write sector, drive #1 0x50: write sector, drive #2 0x80: verify sector, drive #1 0x90: verify sector, drive #2 |
|
| 4 | <command byte 1> | echo cmd1 | |
| 5 | msaddr | ms sector address (cmd2) | |
| 6 | msaddr | echo cmd2 | |
| 7 | lsaddr | ls sector address (cmd3) | |
| 8 | lsaddr | echo cmd3 | |
| 9 | <status> | 0x00 means OK, otherwise an error has occurred | |
| ... rest of sequence | |||
| status byte | meaning |
|---|---|
| 0x00 | OK |
| 0x01 | illegal sector address, or missing media, or miscompare, or write data LRC error |
| 0x02 | sector error |
| 0x04 | CRC error |
| 0x80 | compare |
| 0xC0 | reinit response |
Read a Sector
| step | 2200 sends | disk sends | comment |
|---|---|---|---|
| 1 | 0x00* | CAX re-init request | |
| 2 | 0xC0 | re-init request acknowledgment | |
| 3 | 0x00 | read command (cmd1) | |
| 4 | 0x00 | echo cmd1 | |
| 5 | msaddr | ms sector address (cmd2) | |
| 6 | msaddr | echo cmd2 | |
| 7 | lsaddr | ls sector address (cmd3) | |
| 8 | lsaddr | echo cmd3 | |
| At this point the disk controller checks the legality of the sector address, starts up the motor if it isn't already running, checks that there is a disk in the drive, steps the head to the desired track, then keeps reading sectors from the track until the one matching the desired sector is encountered. | |||
| 9 | <status> | 0x00 means OK, otherwise an error has occurred | |
| 10 | 0x00 (*) | data ignored; abort to step 2 if CAX | |
| 11 | <status> | status byte 0x00: OK 0x02: sector error 0x04: CRC error |
|
| 12 | data[0] | first byte of sector | |
| 13 | data[1] | second byte of sector | |
| ... | ... | ||
| 14 | data[255] | last byte of sector | |
| 15 | LRC | checksum | |
Write a Sector
| step | 2200 sends | disk sends | comment |
|---|---|---|---|
| 1 | 0x00* | CAX re-init request | |
| 2 | 0xC0 | re-init request acknowledgment | |
| 3 | 0x40 | write command (cmd1) | |
| 4 | 0x40 | echo cmd1 | |
| 5 | msaddr | ms sector address (cmd2) | |
| 6 | msaddr | echo cmd2 | |
| 7 | lsaddr | ls sector address (cmd3) | |
| 8 | lsaddr | echo cmd3 | |
| The disk controller verifies the sector address; spins up the disk; checks that a disk is in the drive; steps to the appropriate track | |||
| 9 | <status> | 0x00 means OK, otherwise an error has occurred | |
| 10 | data[0](*) | first byte of sector; abort to step 2 if CAX | |
| 11 | data[1](*) | second byte of sector; abort to step 2 if CAX | |
| ... | ... | ||
| 12 | data[255](*) | last byte of sector; abort to step 2 if CAX | |
| 13 | LRC | checksum | |
| time passes -- the sector is written to the disk | |||
| 14 | <status> | final status; 0x00 if completed successfully; not 0x00 if LRC or other error | |
Verify a Sector
Verify sector is a hybrid of read and write. The beginning of the sequence is identical to the read sector sequence; in fact, the microcode has a common path for verify and the read sector routines. Once the sector has been read successfully, the 2200 CPU sends the data it is expecting. The disk controller verifies each byte against what was just read off the disk and if after the 256 data bytes and LRC checksum byte have been received, if any of the 256 data bytes mismatch what was on the disk, an error code of 0x01 is returned, otherwise it's 0x00.
| step | 2200 sends | disk sends | comment |
|---|---|---|---|
| 1 | 0x00* | CAX re-init request | |
| 2 | 0xC0 | re-init request acknowledgment | |
| 3 | 0x80 | verify command (cmd1) | |
| 4 | 0x80 | echo cmd1 | |
| 5 | msaddr | ms sector address (cmd2) | |
| 6 | msaddr | echo cmd2 | |
| 7 | lsaddr | ls sector address (cmd3) | |
| 8 | lsaddr | echo cmd3 | |
| spin up disk, step to appropriate track | |||
| 9 | <status> | 0x00 if OK, otherwise an error has occurred | |
| 10 | 0x00 (*) | data ignored; abort to step 2 if CAX | |
| time passes -- the sector is read from the disk | |||
| 11 | <status> | status byte 0x00: OK 0x02: sector error 0x04: CRC error |
|
| 12 | data[0] | first byte of sector | |
| 13 | data[1] | second byte of sector | |
| ... | ... | ||
| 14 | data[255] | last byte of sector | |
| 15 | LRC | checksum, but ignored by disk controller | |
| 16 | <status> | final status; 0x00 if OK, 0x01 if mismatch | |