AT&T 3B2 Revision 2 Internals

Table of Contents

Introduction and Overview

The purpose of this page is to document the internals of the AT&T 3B2 Revision 2 line of computers, specifically the AT&T 3B2/310 and AT&T 3B2/400. It started in 2015 as a set of notes that I kept for myself while trying to reverse engineer the 3B2/400. Since then, I’ve managed to track down additional documentation and resources that have helped expand my understanding quite a bit.

Nevertheless, here is still scant information online about the internals of the 3B2. It has proven very difficult to find detailed documentation about how the 3B2 actually works. This page attempts to address that by collecting what I have learned into one place.

But why the 3B2? In large part because it is historically significant, and yet not very well known. The 3B2 was AT&T’s main porting platform for System V Release 3 UNIX™ (SVR3). It is a fully 32-bit computer system built around a microprocessor and chipset by Western Electric called the WE32100. The WE32100 and its earlier predecessor, the WE32000 (collectivey known as BELLMAC-32 processors), represented major leaps forward in building architectures specially suited for UNIX.


In addition to lots of probing of a 3B2/400 system board with an oscilloscope and a logic analyzer, the following documents have been invaluable in this work.

  1. The WE 32100 Microprocessor Information Manaual, Document number 451-000, AT&T, 1985
  2. WE 32-Bit Microprocessors and Peripherals Databook, A&T, 1987
  3. AT&T 3B2 Computer Technical Reference Manual, Document number 305-490, AT&T, 1990
  4. Hardware Configurations and I/O Protocol of the WE32100 Microprocessor Chipset, M. L. Fuccio, L. G. Goyal, B. Ng, AT&T Bell Laboratories
  5. Hardware Architecture Considerations in the WE32100 Chip Set, M. L. Fuccio, B. Ng, AT&T, “IEEE Micro”, April 1986
  6. 3B™2 Component Requirements and Specifications: I/O Bus, Internal Memo, P.M. Walsh, AT&T, 1983
  7. 3B™2 Circuit Description: Common I/O Hardware, Issue 1, Internal Memo, P.M. Walsh, AT&T, 1983
  8. AT&T 3B2 and 3B5 Computer Driver Design Guide, Document number 305-495, AT&T, 1984
  9. AT&T UNIX System V Release 3 Source Code, AT&T, 1985-1988
  10. AT&T 3B2 ROM Images (Link to GitHub)
  11. Disassembled Model 400 ROM image (Link to GitHub)
  12. “The Design of the UNIX Operating System”, Maurice J. Bach, 1986

System Board

Major Components

The 3B2/400 amd 3B2/310 share the same system board. The major parts of the architecture include:

In addition to these major components, there is a tremendous amount of LSI glue logic, plus 14 PLAs/PALs on the system board. These implement the DRAM controller, Control Status Register, and Interrupt Logic. Their exact layout is unknown, as no schematics are available, however the system board block diagram below helps document the over-all architecture at a high level.

Finally, there is one 18-pin DIP package IC labeled with in-house part number “O WE 62C”, stamped with a date code and the number “53”. The chip remains unidentified. Presumably it is custom LSI logic.


Memory Map

ROM Space

The ROM is mapped too the bottom 64KB of the address space, at physical addresses 0x000000000x0000ffff. The ROM itself is only 32KB, so I still need to do some additional diagnostic programming to determine exactly how the ROM maps to the top half of its space, if at all.

When running under SVR3, the ROM is mapped at virtual address 0x00020000.

I have decoded and disassembled the ROM, and discovered that there are several distinct areas

The Vector Tables are described in detail in the SVR3 source code, in the header file sys/firmware.h.

Special RAM Locations

ROMPointerSVR3 NameDescription
0x48c0x02000864p_runflagFlag indicating whether system is safe for booting
0x4900x02001514p_edtPtr to Equipped Device Table (EDT)
0x4940x020011f8p_inthandPtr to location containing address of interrupt handlers
0x4980x020011f4p_exchandPtr. to location containing address of exception handler
0x49c0x02000858p_rsthandPtr. to location containing address of reset handler
0x4a00x02001200p_cmdqueuePtr. to command queue for boot command
0x4a40x020012a8p_fl_consPtr. to float cons struct
0x4a80x0200126cp_optionPtr. to Ptr. to Option Number (for getedt)
0x4c40x02001268p_accessAccess permissions for printf, etc.
0x4e00x020011f0p_num_edtPtr. to location containing number of devices in EDT
0x4e40x020011ecp_memsizePtr. to location containing size of main memory
0x4e80x02001504p_memstartPtr. to location containing start of main memory for UNIX
0x4f00x02000a80p_physinfoPtr. to disk physical info
0x4f40x02001258p_pswstorePtr. to PSW before exception or interrupt
0x4f80x0200125cp_pcstorePtr. to PC before exception or interrupt
0x4fc0x020011e8p_consolePtr. to pointer to console UART
0x5040x02001264p_save_0Ptr. to %r0 before exception
0x50c0x020012d8p_bpthandPtr. to location containing address of exception handler
0x5100x020012d0p_spwrinhLocation for soft power inhibit
0x5140x0200086cp_meminitLocation for memory init flag
0x52c0x020012dcdmn_vexcDemon virtual process / stack exception handler PCB
0x5300x020012e0dmn_vgateDemon virtual gate table location
0x5340x020012e4dmn_vsintDemon virtual stray interrupt PCB location
0x5440x02000a74p_hdcspecLocation of fw hdc spec params

ROM Routines

0x4ac0x00004dd4p_getedtRoutine to fill EDT structure
0x4b00x000044e4p_printfLocation of printf routine
0x4b40x00004360p_getsLocation of gets routine
0x4b80x00004ae4p_sscanfLocation of sscanf routine
0x4bc0x00007f68p_strcmpLocation of strcmp routine
0x4c00x00002af8p_excretRoutine to set up a return point for exceptions
0x4c80x00004484p_getstatRoutine to check console for a character present
0x4cc0x00005320p_chknvramLocation of routine to verify checksum over NVRAM
0x4d00x00005224p_rnvramLocation of routine to read NVRAM
0x4d40x000052a0p_wnvramLocation of routine to write NVRAM
0x4d80x00007698p_hd_csLocation of routine to access hard disk
0x4dc0x00007b2cp_fd_csLocation of routine to access floppy disk
0x4ec0x00001168p_releasePointer to location containing release of code
0x5000x00003d74p_setbaudPointer to setbaud routine
0x5080x00007ff0p_sernoPointer to serial number struct
0x5180x00005438p_bzeroPointer to memory zero routine
0x51c0x00005450p_setjmpPointer to setjmp routine
0x5200x000054a1p_longjmpPointer to longjmp routine
0x5240x00004e14p_dispedtPointer to display edt routine
0x5280x00005504p_hwcntrpointer to DUART counter delay routine
0x5380x000055ecp_fw_sysgenPointer to generic sysgen routine
0x53c0x00005baap_ioblk_acsPointer to ioblk_acs routine
0x5400x000051d2p_brkinhPointer to break inhibit routine
0x5480x0081e100p_symtellFunction to tell xmcp where the symbol table is
0x54c0x0000421fp_demonFunction to enter demon without init

String Locations

There’s a large block of string literals in the ROM which are referenced by the various subroutines. I have added these to the ROM source code as I’ve found them.

IO Space

Looking through the SVR3 source code has revealed the following system board map, in the file usr/src/uts/3b2/vuifile.

unxsbdst0x40000System Board Start Address
mmusdc10x40000MMU SDC bits 0-31
mmusdc20x40100MMU SDC bits 32-64
mmupdc1r0x40200MMU Right Half PDC Bits 0-31
mmupdc2r0x40300MMU Right Half PDC Bits 32-63
mmupdc1l0x40400MMU Left Half PDC Bits 0-31
mmupdc2l0x40500MMU Left Half PDC Bits 32-63
mmusrama0x40600MMU Section RAM A
mmusramb0x40700MMU Section RAM B
mmufltcr0x40800MMU Fault Code Register
mmufltar0x40900MMU Fault Address Register
mmucr0x40a00MMU Configuration Register
mmuvar0x40b00MMU Virtual Address Register
sbdpit0x42000Programmable interval timer (8253)
clrclkint0x42013Clear clock interrupt
sbdnvram0x43000NVRAM (0x400 bytes)
sbdrcsr0x44000CSR Read (reads the whole 16-bit halfword)
sbdwcsr0x44000CSR Write base address
dmaid0x45000DMA Integrated Disk (hard disk) page buffer
dmaiuA0x46000DMA UART A page buffer
dmaiuB0x47000DMA UART B page buffer
dmac0x48000DMA controller status/command register
duart0x490002681 UART
idisk0x4a0007261 Disk Controller
ifloppy0x4d0002797 Floppy Controller
dmaif0x4e000DMA Integrated Floppy page buffer

RAM Space

Main memory (“mainstore”) appears to start at 0x2000000 on the 3B2. Interestingly, on SYSVR3 this is mapped to /dev/mainstore.

WE32100 CPU

As stated, the CPU is the WE32100 32-bit CPU. It has both a 32-bit address bus and a 32-bit data bus. The CPU has 9 general purpose and 7 special purpose 32-bit registers, supports four privilege modes (User, Supervisor, Executive, Kernel), supports addressing in words (32-bit), halfwords (16-bit), and bytes (8-bit), and has a highly orthogonal instruction set with many addressing modes. It is paired with the WE32101 MMU, the WE32102 dual-phase crystal oscillator which provides two 10MHz clocks 90° out of phase, and the optional WE32106 Math Accelerator UNIT.

WE32101 MMU

The WE32101 MMU provides virtual address to physical address translation using both segmented and paged memory.

On the 3B2, virtual address space is divided into four sections, identified by the top two bits of the address.

The MMU uses these top two bits as a Segment ID (SID) to look up the address in physical memory of a Segment Descriptor Table (SDT).

The next 13 bits of the address are known as the Segment Select field, and are used to index into the Segment Descriptor Table to find a Segment Descriptor.

The Segment Descriptor contains a bit that determines whether the virtual address being translated is to be treated as a Contiguous Segment, or Paged.

The rest of the bits of the virtual address are then treated differently depending on the value of this bit.

This process is described in great detail in the WE 32100 Microprocessor Information Manual and in the WE 32101 MMU datasheet.

Segmented Memory Virtual Addresses

Segmented virtual addresses further divide the virtual address into a Segment Offset (SOT). The physical memory address is then translated as in the figures below.



Paged Memory Virtual Addresses

Paged virtual addresses further divide the lower 17 bits into a 6-bit Page Select field and an 11 bit Page Offset field. The physical memory address is then translated as in the figures below.



8253 Programmable Interval Timer

This timer is responsible for driving much of the interrupt system. Its functions are IO mapped to the following addresses:

Physical Address8253 FunctionSBD Function
0x42003Counter 0Sanity Timer (used for softpower)
0x42007Counter 1Interval Timer
0x4200bCounter 2Not yet known
0x42013Clear Latch

Control Status Register

This is just a set of latches that can be set or cleared by writing to various memory locations, or read as a single 16-bit value. The base address of the CSR is at 0x44000.

Two 74LS374 Octal D-Type latches provide the 16-bit CSR.

The bits and addresses of the CSR are described in detail in Figure 3-22 of the AT&T 3B2 Computer Technical Reference Manual, on page 3-68:

150x44000ClearError Timer Timeout (Bus Error)
140x44004ClearMemory Parity Error
130x44008SetSystem Reset Request
120x4400cClearClear memory alignment trap
110x44010SetDiagnostic indicator (Green LED) On
110x44014ClearDiagnostic Indicator (Green LED) Off
100x44018SetFloppy Motor On
100x4401cClearFloppy Motor Off
80x44020SetInhibit Timers
80x44024ClearAllow Timers
70x44028SetInhibit Faults
70x4402cClearAllow Faults
60x42010ClearClear Periodic Timer Interrupt (INT 15)
50x44038SetSet PIR 8 Interrupt
50x4403cClearClear PIR 8 Interrupt
40x44030SetSet PIR 9 Interrupt
40x44034ClearClear PIR 9 Interrupt
3N/ARead OnlyUART Interrupt
2N/ARead OnlyFloppy Disk Interrupt
1N/ARead OnlyDMA Interrupt
0N/ARead OnlyI/O Board Fail

The 16 bits of the CSR, when read, map to the following bitmasks


2681 Dual UART

The UART provides two serial ports integrated into the main system. One is the console, the other is a secondary TTY. Additionally, the UART provides a general purpose counter/timer circuit with an interrupt output.

The UART is clocked at 230.525 KHz. Due to the 16-bit counter, the maximum delay for the UART interrupt timer is 284 ms.

Floppy Drive

The standard floppy drive (known as IF, “Integrated Floppy”) is a CDC 9429. The floppy format used is a little unusual: It is a Double-Sided, Quad Density format (DSQD) holding 720KB per diskette. The format is:

Winchester (MFM) Hard Disk Controller

The 3B2/310 and 3B2/400 support up to two MFM encoded Winchester hard disks using the ST-506 interface. On the 3B2/310, the ST-506 control and second data bus are exposed on a header on the back of the computer that can be connected to an expansion chassis with a second hard disk. On the 3B2/400, both disks can be connected internally.

Large Disk Support Hack

The original ST-506 standard only supported a maximum of 4 heads, using two head select lines. Later, a third head select line was added to enable support for up to 8 heads per drive. Later still, drives appeared that supported more than 8 heads by adding a fourth head select line.

The 3B2 uses an NEC μPD7261A hard drive controller with only three head select lines. However, the system supports drives with more than 8 heads, using a kludge.

The way it works is by stealing two of the drive select lines, since there are four but only two drives are supported. Drive select lines 0, 1, 2, and 3 go through a pair of OR gates, such that 0 and 2 go to the first Winchester, and 1 and 3 go to the second Winchester.

Additionally, drive select lines 2 and 3 go through another OR gate and are electrically connected to Head Select 3.

The low level disk driver in System V UNIX knows about this. If the operating system requests a head >= 8, it will actually increment the unit number it’s selecting. From the source code:

#define IDMAXHD             0x08
#define IDADDEV             0x02

/* if the head number is greater than 7 */
if (idxferparam[unit].phn >= IDMAXHD) {
    idxferparam[unit].unitno += IDADDEV;

Using this trick, the integrated disk controller in UNIX always adds 2 to the unit number whenever the head requested is greater than 7. That way, if the currently selected drive is drive number 0, head 8 and above engage drive select 2. If the currently selected drive number is 1, head 8 and above engage drive select 3.

Expansion Cards

Expansion cards, or “Feature Cards”, are IO devices that can be added to the 3B2’s IO bus. The 3B2/310 has four slots, while the 3B2/400 has twelve.

Cards are automatically probed and configured at startup.


Each intelligent feature card has four 8-bit registers:

The vectors for each slot map to the following addresses:


The Equipped Device Table (EDT)

The Equipped Device Table (EDT) is a structure in main RAM that describes the installed hardware. It holds information about all installed devices.

Basic Structure

The EDT lives in main memory at 0x2001514, referenced by ROM pointer p_edt at ROM vector address 0x490.

The EDT holds information about cards on the IO bus discovered at start-up. Each slot is 128 bytes long (4 words), and uses the following structure:

FieldWidth (bits)Description
opt_code16Option code
opt_slot4Slot number the board is in
opt_num4Which of given option types the board is
rq_size8Request queue entry size
cq_size8Completion queue entry size
resrvd14Reserved for future use
cons_cap11 = can support console, 0 = cannot
cons_file10 = no pump file for console, 1 = does
boot_dev11 = has possible boot device
word_size10 = 8-bit, 1 = 16 bit
brd_size10 = single width, 1 = double width
smrt_brd10 = dumb board, 1 = smart board
n_subdev4Subdevice count
subdev32Pointer to array of n_subdev subdevice structures
dev_name1010-byte (including terminator) name
diag_file10Name of resident file containing diag. phases
padding12Padding for word alignment

Note that in the table, slot entries are aligned on 8-word boundaries. So the first slot 0, the system board (SBD) is at 0x2001514, while slot two appears at 0x2001534, slot three at 0x2001554, and so on.

Subdevice structures are very simple 4-byte entries.

FieldWidth (bits)Description
opt_code16Option code
name10Option name
padding6Padding for word alignment

Detecting Devices

The ROM startup code is responsible for filling the skeleton of the EDT with option codes and slot numbers, and for keeping track of the total number of entries.

On start-up, it begins by filling in the SBD entry in slot 0. Then it queries each card address from slot 1 to slot 12, one by one, probing for cards. It does so by writing the byte 0x00 into the card’s command register (the slot’s base address + 5), then by reading the card’s option ID in two bytes: the high byte of the ID from the base address, and the low byte of the ID from base address + 1.

If an External Memory Exception occurs during either write or read, and the TIMEO bit is set in the CSR indicating that a bus timeout occurred, it is assumed that no card is available in the slot and it is removed from the EDT.

The following is a listing of IO Slot base addresses.

Slot No.Base Address
0N/A (SBD)

Startup Procedure

Sysgen Block

struct sysgen {
    long request;            /* address of request queue */
    long complt;             /* address of completion queue */
    unsigned char req_size;  /* number of entries in request queue */
    unsigned char ceq_size;  /* number of entries in completion queue */
    unsigned char int_vec;   /* base interrupt vector */
    unsigned char no_rque;   /* number of request queues */

request and complt both point to queue structures in memory, request queue and completion queue structures respectively.

typedef struct {
    ENTRY express;         /* Express Entry */

    struct {
        union {            /* Load and Unload pointers */
            uint32_t all;
            struct {
                uint16_t load;
                uint16_t unload;
            } bit16;
            struct {
                uint8_t pad1;
                uint8_t load;
                uint8_t pad2;
                uint8_t unload;
            } bit8;
        } p_queues;

        /* the queue entris RQSIZE or CQSIZE */
        ENTRY entry[qsize];
    } queue[num_queues];

Each entry in the queue uses the following structure:

typedef struct {
    uint16   byte_count;  /* # of bytes to transfer */
    uint8    subdevice;   /* Subdevice number */
    uint8    opcode;      /* Opcode/Return code of command */
    uint32_t address;     /* Address / Data */
    uint32_t app_data;    /* App-specific data */

PORTS Card Example

The following is a very high level of how the PORTS expansion card starts up and operates.

1. Determine Version

The first step is to sysgen the card and establish two-way communication queues between the card and the system board.

  1. The system board performs a normal RESET / INT0 / INT1 sysgen sequence.
  2. The PORTS card responds with an Express Queue response and an interrupt with IPL 10 and the appropriate vector.
  3. The system board requests the CARD’s version with a PPC Version request (opcode 80)
  4. If the PORTS card is ROM version 1, it doesn’t know how to respond, so completes with an error code (Response code 2). If the PORTS card is ROM version 2, it responds by writing the version to the correct place in memory, set by the request.

At this point, the card is properly sysgen’ed, and the system board knows what version it is.

2. Download Pump Code

The actual firmware of the PORTS card is stored on the 3B2’s hard disk in the file /lib/pump/ports. This code, called the pump code, is uploaded to the card and executed through a request from the system board.

  1. Full RESET/INT0/INT1 Sysgen sequence.
  2. Express Completion Queue reply (IPL 10)
  3. The system board begins downloading the file /lib/pump/ports to the card’s memory using a series of Download Memory Express Queue requests (opcode 1)
  4. After each incremental download request, the PORTS card responds with an Express Queue entry and an interrupt at IPL 10 and the appropriate vector.
  5. After all segments of the file /lib/pump/ports have been downloaded to the card, the system board initiates a Force Function Call request to the card, telling the card to start running code at address 0x000500.
  6. The PORTS card responds with an Express Queue response and an interrutp with IPL 10 and the appropriate vector.

The card is now ready to serve requests.

3. Serving Requests

The following is an example of what occurs when the user types $ echo "foo" > /dev/tty11 at the shell. Each element represents either a Request Queue entry or a Completion Queue entry.

First, the system board issues a Connect command for the requested card and port.

BYTES: 0x00, OP: 0x22, SUBDEV: 0x00, ADDR: 0x00000000, APP: 00 00 00 00
BYTES: 0x00, OP: 0x22, SUBDEV: 0x00, ADDR: 0x00000000, APP: 00 00 00 00

If the port has a DTR signal and is ready to transmit data, it responds out-of-band with an Asynchronous completion in addition to the Connect completion.

Here, the bit 0x01 in the first byte of the App Data means that a connection has been detected on the requested port/subdevice (0).

BYTES: 0x00, OP: 0x3c, SUBDEV: 0x00, ADDR: 0x00000000, APP: 00 00 00 00

Now the system board sends an Options command.

The PORTS board reads the Options structure at the supplied address (0x18 bytes long) and configures itself.

BYTES: 0x18, OP: 0x20, SUBDEV: 0x00, ADDR: 0x020AFE46, APP: 00 00 00 00

BYTES: 0x18, OP: 0x20, SUBDEV: 0x00, ADDR: 0x020AFE46, APP: 00 00 00 00

Now the system board sends a series of seven Receive requests. Note that the PORTS card does not immediately respond to these requests. It queues them up for later processing.

BYTES: 0x3F, OP: 0x32, SUBDEV: 0x05, ADDR: 0x020AFE46, APP: 00 00 00 00
BYTES: 0x3F, OP: 0x32, SUBDEV: 0x05, ADDR: 0x020AFED6, APP: 00 00 00 00
BYTES: 0x3F, OP: 0x32, SUBDEV: 0x05, ADDR: 0x020AFF66, APP: 00 00 00 00
BYTES: 0x3F, OP: 0x32, SUBDEV: 0x05, ADDR: 0x020AFF1E, APP: 00 00 00 00
BYTES: 0x3F, OP: 0x32, SUBDEV: 0x05, ADDR: 0x020AFDFE, APP: 00 00 00 00
BYTES: 0x3F, OP: 0x32, SUBDEV: 0x05, ADDR: 0x020AFDB6, APP: 00 00 00 00
BYTES: 0x3F, OP: 0x32, SUBDEV: 0x05, ADDR: 0x020AFD6E, APP: 00 00 00 00

After transmission is complete, the system board sends a series of express Device requests with different values in the application data.

Here, 0x07 in byte 0 of the Application Data means DR_RES, or “Resume Transmit on a Device”. 0x03 in byte 0 of the Application Data means DR_ABR, or “Abort Reception on a Device”.

On completion, the received application data is placed in byte 1 of the Completion Queue Entry’s Application Data, and a response code (in this case, 0x00 for “Executed Normally”) is placed in byte 0.

BYTES: 0x00, OP: 0x28, SUBDEV: 0x00, ADDR: 0x00000000, APP: 07 00 00 00
BYTES: 0x00, OP: 0x28, SUBDEV: 0x00, ADDR: 0x00000000, APP: 00 07 00 00
BYTES: 0x00, OP: 0x28, SUBDEV: 0x00, ADDR: 0x00000000, APP: 03 00 00 00
BYTES: 0x00, OP: 0x28, SUBDEV: 0x00, ADDR: 0x00000000, APP: 00 03 00 00

The board sends another Device request with the command “Abort Reception”, immediately followed by a Disconnect before the board has a chance to respond to the Device.

In the Disconnect command, the Application Data byte 1 value 0x03 is the OR’ed combination of GR_DTR (0x01) and GR_CREAD (0x02). The value 0x07 is the number of CBLOCKS to be returned to the system (matching the 7 requests above)

BYTES: 0x00, OP: 0x28, SUBDEV: 0x00, ADDR: 0x00000000, APP: 03 00 00 00

BYTES: 0x00, OP: 0x23, SUBDEV: 0x00, ADDR: 0x00000000, APP: 07 03 00 00

Then, we get the response to the previous Device request.

BYTES: 0x00, OP: 0x28, SUBDEV: 0x00, ADDR: 0x00000000, APP: 00 03 00 00

Now we get a series of responses to the Receive requests made above. These seem to be in no predictable order.

Additionally, each of these Receive requests is followed by the system board issuing an INT1 (Attention) interrupt to the PORTS board.

BYTES: 0x3F, OP: 0x32, SUBDEV: 0x00, ADDR: 0x020AFE8E, APP: 01 00 00 00
BYTES: 0x3F, OP: 0x32, SUBDEV: 0x00, ADDR: 0x020AFF66, APP: 01 00 00 00
BYTES: 0x3F, OP: 0x32, SUBDEV: 0x00, ADDR: 0x020AFE46, APP: 01 00 00 00
BYTES: 0x3F, OP: 0x32, SUBDEV: 0x00, ADDR: 0x020AFDFE, APP: 01 00 00 00
BYTES: 0x3F, OP: 0x32, SUBDEV: 0x00, ADDR: 0x020AFDB6, APP: 01 00 00 00
BYTES: 0x3F, OP: 0x32, SUBDEV: 0x00, ADDR: 0x020AFD6E, APP: 01 00 00 00
BYTES: 0x3F, OP: 0x32, SUBDEV: 0x00, ADDR: 0x020AFED6, APP: 01 00 00 00

Finally, the last message is the completion of the Disconnect sent above, before the Device and Receive messages.

BYTES: 0x00, OP: 0x23, SUBDEV: 0x00, ADDR: 0x00000000, APP: 00 00 00 00


The WE32100 has fairly complex interrupt handling capabilities. Interrupt vectors can be set by the external device, or internally set via an auto-vector. Interrupts may be handled via a “quick-interrupt” handler, which is a simulated GATE instruction, or via a “full-interrupt” handler, which is a full process switch analogous to CALLPS. And finally, there’s an non-maskable interrupt (NMI).

The 3B2 ROM and UNIX SVR3 do not use NMI, auto-vector interrupts, or quick interrupts at all. Running the 3B2 under a logic analyzer revealed that the /NMI, /AVEC, and /INTOPT inputs on the CPU is never asserted, either during ROM initialization or UNIX boot. Everything is handled via the full interrupt sequence.

Moreover, interrupts are completely disabled during ROM initialization, before UNIX boot. The IPL in the processor status word is always set to 15 (1111b), so no interrupts except NMIs will be handled. Interrupts are enabled in in the PSW by the UNIX kernel when it starts the 10ms interval timer.

The ROM area that would normally be used for quick interrupt handlers is instead used for PCBPs.

Below is the full-interrupt vector table in ROM.

0x8C0x02000BC8NMI Handler
0x900x02000BC80Auto-Vector Handler (not used)
0x940x02000BC81PCBPs (31 words)
0xAC0x02000C188Programmed Interrupt 8 (PIR8)
0xB00x02000C689Programmed Interrupt 9 (PIR9)
0xB80x02000D0811Hard Disk & Floppy
0xC80x02000E481510ms interval timer and Syserr
0x10C0x02000BC8Device Interrupt Handler
0x1100x02000BC8PCBPs (224 words)
(All identical PCBPs)

3B2/400 System Board Interrupt Sources

The 3B2 interrupt subsystem takes interrupt inputs from several sources on the system board and translates them into values on the CPU’s four bit IPL (Interrupt Priority Level) bus. The IPL bus is fed by a 74LS148 priority encoder.

The hard disk controller (ID), floppy disk controller (IF), and DUART (IU) interrupt outputs are directly connected to the priority encoder (potentially through inverters), and are cleared when the originating device de-asserts its IRQ line. The System Timer, however, is supplied to the priority encoder through a latch that is reset by software. Likewise, the CSRPIR8 and CSRPIR9 software interrupts are supplied through the CSR latches, and are reset by software.

IRQs are serviced by the CPU if and only if the PSW (processor status word) has an IPL field that is less than the corresponding IPL bus level. For example, if the PSW’s IPL field is set to 15, no IRQs will be processed. If the PSW’s IPL field is set to 12, only IRQs with priority level 13 or 15 will be processed.

An IPL bus level of 15 means that no IRQs are pending.

The 3B2 interrupt subsystem takes input from the following sources:

CSRPIR88NoSoftwareSoftware IRQ
CSRPIR99NoSoftwareSoftware IRQ
Hard Disk11NoHardware
IU Timer13NoHardware
System Timer15YesSoftware10 ms timer


CSRPIR8 and CSRPIR9 are both software interrupts. That is, they are set and cleared by software.

When the corresponding bit in the CSR is set, the IPL bus immediately asserts IPL 8 or IPL 9. When software resets the corresponding CSR bit, the IPL level returns to 15 (no IRQs pending).

Hard Disk and Floppy

The uPD7261A hard disk controller’s INT output and the WDC2797 INTRQ output are both tied directly to the priority encoder and trigger IPL 11 on activation. As long as either output is asserted, IPL 11 will be on the IPL bus. The IPL bus is restored to level 15 when the INT or INTRQ output is deasserted.

Assertion of the WDC2797’s INTRQ output also sets the CSRDISK bit in the CSR. Software can differentiate between interrupts generated by both controllers by checking this bit in the CSR. (TODO: How is the CSRDISK bit cleared? Is it cleared when CSRFLOP bit, connected to the drive motor, is cleared?)


The 2681 dual UART’s INTRN output is tied directly to the priority encoder and triggers IPL 13 on activation. As long as the INTRN output is asserted, IPL 13 will be on the IPL bus. The IPL bus is restored to level 15 when INTRN is de-asserted.


The DMAC can generate an interrupt at IPL 13 via the CSRDMA bit in the CSR by pulling down its EOP output, which, if associated with a UART Rx or Tx (signaled by the DUART’s OP0/OP1 pins), will be latched in the CSR. Note that ONLY DUART transfers cause EOP to be latched as CSRDMA. No other DMAC EOP assertion will cause an interrupt at IPL 13. (TODO: I’d like to trace out exactly how this is wired, to better understand how OP0/OP1 and EOP are combined into the latched CSRDMA bit)

System Timer

The system timer is an 8253 interval timer with three independent 16-bit counters and corresponding timer outputs. Output 1 is latched and causes an interrupt at IPL 15. (TODO: trace and figure out where it’s latched) In the SVR3, this timer output is used as the 10ms/100Hz system timer, and drives process switching and general housekeeping tasks in the operating system.

Miscellaneous Notes

Installation Floppy Format

Software distributed on floppy has the following requirements:

  1. The diskette label should be the short name of the product, e.g. “sgu” for Software Generation Utilities“ or “nsu” for “Network Service Utilities”
  2. The filesystem should be a normal S51K (System V 1K) filesystem
  3. The filesystem should be named “instal” or “/instal”, because it is mounted on the /install mountpoint by default.

Compiling a debug version of lboot

The first step is to change the load map (lbld) to incrase the size of the lbcode section:

        lbcode: origin = 0x020ef000, length = 0x13000
        lbbss:  origin = 0x02102000, length = 0x3c00
        lbstack:origin = 0x020ed000, length = 0x2000
        .text:  {
                stext = .;
                }  > lbcode
        .data:  {} > lbcode
        .bss:   {} > lbbss
                sstack = .;
                estack = .+0x2000;
                }  > lbstack

Then, edit and uncomment the line:

        -DDEBUG1 \
        -DDEBUG1f -DDEBUG1g -DDEBUG1h -DDEBUG1i \

It can also be nice to uncomment the line


so you get a memory map printed out on completion.

Installing a new lboot and mboot

These are the initial boot programs on the hard disk. mboot is responsible for loading lboot, and lboot is responsible for loading the kernel, or generating a new kernel.

After compiling a new lboot or mboot, they can be installed with this command:

# newboot /lib/lboot /lib/mboot /dev/idsk06

Appendix A: System Board Components

The System Board has 157 integrated circuits and 2 crystal oscillators. The table below summarizes the parts.

Major Components


In this figure, numbers are somewhat arbitrarily ordered, but roughly from top to bottom, right to left.

3WE32106Math Accelerator Unit
10WE32102CPU clock source
4,5,6,7i2764System ROM
88253Interval timer interrupt source
57TMS2797Floppy disk controller
93AM9517AMultimode DMA controller
942681Dual UART and interval timer interrupt source
156μDP7261Hard disk controller
AHeaderFloppy disk connector
BHeaderMFM hard disk data connector
CHeaderMFM hard disk data connector
DHeaderMFM hard disk control connector
E,FConnectorBackplane card edge connector
GMemoryMemory slot A (1MB,2MB)
HMemoryMemory slot B (1MB,2MB)

All Parts

26811Dual UART
26LS321Quad differential line receiver
27LS311Quad differential line driver
74062Hex inverter buffer drivers with O.C. outputs
74072Hex buffer drivers with O.C. outputs
74F041Hex inverters
74F111Triple 3-input AND gates
74LS001Quad 2-input NAND gates
74LS021Quad 2-input NOR gates
74LS045Hex inverters
74LS051Hex inverters with O.C. outputs
74LS082Quad 2-input AND gates
74LS091Quad 2-input AND gates with O.C. outputs
74LS101Triple 3-input NAND gates
74LS111Triple 3-input AND gates
74LS1121Dual neg.-edge-triggered master/slave J-K flip-flops
74LS1251Quad bus buffers with tri-state outputs
74LS1261Quad bus buffers with tri-state outputs
74LS13863-to-8 line decoder/demux
74LS1391Dual 2-to4 decoder/demux
74LS142Hex inverter with Schmitt trigger inputs
74LS14818-to-3 priority encoder
74LS15118-input multiplexer
74LS1571Quad 2-input multiplexers
74LS16418-bit serial-in/parallel-out shift reg.
74LS1741Hex D-type flip-flops
74LS213Dual 4-input AND gates
74LS2445Non-inverting buffer/driver
74LS2577Quad 2-input multiplexers with tri-state outputs
74LS2794Quad SR Latches
74LS325Quad 2-input OR gates
74LS3746Octal D-Type latches with tri-state outputs
74LS3901Dual decade counter
74LS3932Dual 4-state binary counter
74LS6459Octal bus transceiver
74LS6464Octal bus transceiver with tri-state output
74LS745Dual D-type flip-flop
74S002Quad 2-input NAND gates
74S021Quad 2-input NOR gates
74S031Quad 2-input NAND gates with O.C. outputs
74S041Hex inverters
74S051Hex inverters with O.C. outputs
74S083Quad 2-input AND gates
74S101Triple 3-input NAND gates
74S1121Dual neg.-edge-triggered master/slave J-K flip-flops
74S1742Hex D-type flip-flops
74S1754Quad D-type flip-flops
74S322Quad 2-input OR gates
74S748Dual D-Type positve edge triggered flip-flops
751881Quad RS-232 line drivers
751891Quad RS-232 line receivers
82S15312Field programmable logic array (PLA)
974-6033-0120 MHz xtal osc.
AM298435Bus interface latches
AM298535Parity bus transceiver
AM9517A1Multimode DMA controller
AMPAL162Programmable array logic (PAL)
D8253C1Programmable interval timer
MM58174A1Real time clock
TMS27971Floppy disk controller
μDP72611Hard disk controller
WE321021Bi-phase 10MHz xtal osc.
WE62C1Unknown (marked “O WE 62C”)