Vintage PC pages
Home -> Vintage PCs -> Wang Professional Computer

The Wang Professional Computer

The Wang Professional Computer is one of the first generation of competitors to the IBM PC: an MS-DOS machine that isn't compatible with the IBM at a hardware level.

I wrote the bulk of this page based on reverse-engineering, before discovering a copy of the Technical Reference manual online at Corrections from the Technical Reference manual have been applied where appropriate.


The case is roughly the size of an IBM XT, sideways on, but taller. At the front are two full-height 5.25" drive bays; the right-hand one holds a 5.25" floppy drive, and the left-hand one holds a Quantum Q540 hard drive, formatted to 32Mb (the boot ROM refers to this as a 'Winchester', as this page will from now on). From the design of the case, a twin-floppy configuration would also be possible.

[Wang PC from the front]

A large power supply occupies the left-hand rear corner of the case. The remainder of the bottom level of the case contains the L-shaped motherboard, which contains the processor, memory, floppy controller, serial port, parallel port and keyboard interface.

The motherboard plugs into a vertical backplane which is attached to the side of the power supply. This backplane provides six slots: one for the motherboard, and five for expansion cards.

[Wang PC from the back]

Cards, from top to bottom:

The cards then occupy the right-hand side of the case (as seen from the front). While they aren't as big as the motherboard, they are still much bigger than the cards in IBM PCs — about twice the size. The Winchester controller normally occupies the top slot, where provision has been made for MFM cables running to the right-hand drive bay.

[Wang and IBM cards]
Top: Full-size ISA video card. Bottom: Full-size Wang video card.

At the I/O level, each slot occupies a different range in the processor's address space. Motherboard devices have addresses betweeen 1000h and 10FFh; then the device in the first slot appears at 1100h-11FFh, the second at 1200h-12FFh and so on. The boot ROM scans fifteen address ranges (11xxh - 1Fxxh). This system ensures that I/O ranges used by different cards will not clash, and allows a system to support (for example) multiple display cards.

The high bit of the value read from xxFEh is set if the device is interrupting. An interrupt service routine should check all cards to see which one the interrupt came from.

The low 7 bits of the value read from xxFEh give the device class, which can include:

System Card
PM029 Winchester card
APC System Card
Colour video card (40×25)
11h / 15h
PM001-B Monochrome video card (80×25), or PM101 IBM Mono Emulation board
13h / 17h
Text / Image / Graphics video card
IBM-compatible CGA (80×25)
Multi-Terminal Controller
PM040-B remote communications
WTMX Diagnostics
X.21 remote communications
Multiport Communications option
3278 Interface Card
SCTD Controller Card
Remote IPL Card
X.21 Multiport Communications card
Wang Personal Scanner
PM070 local interconnect
High Speed Expanded Memory
Local Communications Option
CP/M-80 emulation card
PM031-B memory expansion
Scanner / Printer Card
Laser Printer Controller
Secure Local Communications Option
Wang monitor Char/Graph/IBM Card
Wang medium resolution color graphics

This port can also be written to; on the Winchester card, for example, it configures DMA access.

Motherboard devices

Devices on the mainboard include:

Motherboard I/O Ports

Without any technical documentation, I've had to guess most of these...

1000:   Bit 0: Set if the DMA chip should stop the floppy controller when it
        reaches terminal count.
	Bit 1: Set to disconnect the FDC from DMA channel 2, clear to connect
	Bit 2: Set to reset interrupt generated by drive 0 changeline
	Bit 3: Set to reset interrupt generated by drive 1 changeline
1004-1012: Drive selection / motor logic. It does not seem to matter what 
	bytes are written to these ports; any write will do.
1004:	Deselect drive A:
1006:	Select drive A:
1008:	Deselect drive B:
100A:	Select drive A:
100C:	Stop motor for drive A:
100E:	Start motor for drive A:
1010:	Stop motor for drive B:
1012:	Start motor for drive B:
1014:	FDC main status register
1016:	FDC data register
1018:	} Any read or write access to these ports will reset the FDC.
101A:   }	
101C:   } Any read or write access to these ports will send a Terminal Count
101E:   } signal to the FDC.

1020: PPI channel A (input): Centronics port status
	Bit 7: 0 if printer has acknowledged last byte. Reset by writing 
                 port 10ECh.
	Bit 6: 1 if paper out.
	Bit 5: 0 if fault.
	Bit 4: 1 if printer busy.
	Bit 3: 1 if printer selected.
	Bit 2: 0 if character received; the character can be read from port 
	Bit 1: 0 if printer supports bidirectional communication.
	Bit 0: 0 if printer power on.
1022: PPI channel B (input): Source of mainboard interrupt.
	Bit 0: zero if IRQ1 triggered by timer channel 2. Reset by reading 
                port 10E2h.
	Bit 1: zero if IRQ1 triggered by serial port. Reset by servicing the
		interrupt at the SCN2661.
	Bit 2: zero if IRQ1 triggered by parallel port. Reset by reading port
	Bit 3: zero if IRQ2 triggered by the DMA controller reaching terminal
		count. Reset by reading port 10E6h.
	Bit 4: set if IRQ2 triggered by sending byte to keyboard. Reset by 
		writing port 10E6h.
	Bit 5: set if IRQ2 triggered by input from keyboard. Reset by reading
		the scancode.
	Bit 6: set if IRQ2 triggered by the floppy controller. Read port 
		10E0h (bits 3-5) to see what caused the interrupt.
	Bit 7: set if IRQ2 triggered by the 8087.	
1024: PPI channel C (bits 0-3 output, 4-7 input)
	Bit 0: Centronics Auto Feed (active low)
	Bit 1: Centronics Select (active low)
	Bit 2: Centronics Reset (active low)
	Bit 3: Unused
	Bits 4-7: Motherboard DIP switches.
1026: PPI control register (always write 9Bh).

1040: i8253, channel 0 (system tick. Generates interrupts on IRQ0).
1042: i8253, channel 1 (used for memory refresh)
1044: i8253, channel 2 (general purpose clock. Generates interrupts on IRQ1).
1046: i8253, control register

1060: i8259, main register
1062: i8259, initialisation command registers

1080: Serial port (SCN2661): Read receive holding register
1082: Serial port (SCN2661): Read status register
1084: Serial port (SCN2661): Read mode register
1086: Serial port (SCN2661): Read command register
1088: Serial port (SCN2661): Write transmit holding register
108A: Serial port (SCN2661): Write SYN1 / SYN2 / DLE registers
108C: Serial port (SCN2661): Write mode register
108E: Serial port (SCN2661): Write command register

10A0: i8237 DMA channel 0 base address
10A2: i8237 DMA channel 0 word count
10A4: i8237 DMA channel 1 base address  (channel 1 is for Winchester drives)
10A6: i8237 DMA channel 1 word count
10A8: i8237 DMA channel 2 base address  (channel 2 is for floppy drives)
10AA: i8237 DMA channel 2 word count
10AC: i8237 DMA channel 3 base address
10AE: i8237 DMA channel 3 word count
10B0: i8237 DMA status / command register
10B2: i8237 DMA write request register
10B4: i8237 DMA mask register
10B6: i8237 DMA mode register
10B8: i8237 DMA clear flip-flop
10BA: i8237 DMA master clear register
10BC: i8237 DMA clear mask register
10BE: i8237 DMA write mask register
10C0: DMA segment, channel 0
10C2: DMA segment, channel 1
10C4: DMA segment, channel 2
10C6: DMA segment, channel 3

10E0:   When read, the following bits have meaning:
	Bit 0: Normally 1. Becomes 0 if there is a memory parity error.
	Bit 1: Normally 1. Becomes 0 if there is an I/O error.
	Bit 3: Set if the floppy controller interrupted. Reset by performing
	Bit 4: Set if floppy 0 changeline has changed. This flag is reset 
		by writing to port 1000h with bit 2 set.
	Bit 5: Set if floppy 1 changeline has changed. This flag is reset 
		by writing to port 1000h with bit 3 set.
	Bit 6: Floppy 0 changeline: set if drive 0 not ready.
	Bit 7: Floppy 1 changeline: set if drive 1 not ready.

	When written, resets IRQ0 and timer channel 0.

10E2: When read, resets IRQ1 and timer channel 2.
      When written, the bottom bit enables (1) or disables (0) the NMI.
10E4: When read, lights the diagnostic LED.
      When written, the bottom bit enables (1) or disables (0) the 8087 
10E6: Writes to this port clear the keyboard transmit interrupt. 
      Reads clear the DMA controller interrupt.
10E8: Keyboard UART (IM6402) data port.
10EA: Parallel port data latch, bidirectional.
10EC: Write to clear parallel port Acknowledge flag.
      Read to clear parallel port Busy flag.
10EE: Write to clear parity flag. Read to turn the diagnostic LED off.
10FE: Bits 6-0 are always 0. Bit 7 set if FDC is interrupting.

Keyboard scancodes and commands are listed on their own page.

Boot ROM

The boot ROM performs a hardware self-test, and then determines the system console. By default, the keyboard is used for input. Output is sent to a video card (if present) or the serial port (otherwise).

If a bootable device is found, its boot program will be loaded and launched. If not, a menu will be displayed, allowing the system console to be reassigned and the boot device to be chosen. The ROM can boot from either floppy drive, a Winchester, the serial port, or the keyboard (with the boot program typed in as hex bytes).

Unless booting from the keyboard, the boot program needs a valid signature ('Wang' at offset 3) and the 8-bit checksum of the first 512 bytes should be zero using the calculation below:

	xor	ax, ax
	mov	cx, 200h
calc:	adc	al, es:[bx]
	inc	bx
	loop	calc

The boot program will be entered with DH = slot number of boot device.

Note that the boot services and memory layout shown below are not available to programs running under MS-DOS, which overwrites these tables with its own BIOS.

Boot Environment: Interrupts

The following services are provided to the boot program:

INT 80h - INT 87h
Hardware interrupt handlers.
INT 88h
Poll console input. Returns AL=waiting character if present, else 0. Does not remove waiting character from the queue.
INT 89h
As INT 88h, but if a character is waiting, remove it from the queue.
Output character in AL to the console.
Output 0-terminated string at DS:SI to the console.
INT 91h
Read sector(s):
	  AL = number of sectors to read
          BX = first sector number to read
          DL = retry count
          ES:SI = load address

	  Returns AH=80h if OK, else error:
		01h: Drive not ready
		02h: Controller fault (returning results when none expected)
		03h: Read error (eg CRC)
		04h: Format error (No Data / Missing Address Mark)
		05h: Controller failed
		08h: Too many sectors (would cross segment boundary)
		0Bh: Cannot find file.
INT 92h
Set drive geometry.
	  AL = sectors / track
	  AH = heads
	  DX = offset in sectors to start of partition
	  CL = read/write gap
	  CH = sector size: 0=>128, 1=>256, 2=>512, 3=>1024
INT 97h
Display boot menu. BX = flags:
	  Bit  0: Offer G=Go
	  Bit  1: Offer R=Retry
	  Bit  2: Offer D=Choose start device
	  Bit  3: Offer P=Power-up diagnostics
	  Bit  5: Offer Q=Quick Restart
	  Bit 13: Beep
	  Bit 15: Do not print a newline before showing the boot menu.

Boot Environment: Device Table

The boot ROM creates a table of detected devices at 40h:0. Each entry is 8 bytes long, and the table has 20 entries. The first 15 are for devices in slots, while the last five are for motherboard resources.

The format of an entry is:

00	DB	id	;Bit 7 is 0 if device is present and operational.
			;Bits 6-0 give device class (as read from port xxFEh)
01	DB	flags	;Bit 7 set if device self-test failed.
			;Bits 6-4 give device type:
			;  0 => first floppy 
			;  1 => second floppy
			;  2 => keyboard
			;  3 => serial port
			;  4 => parallel port
			;  5 => device with its own driver ROM ('NEWDEV')
			;Bit 3 set if device is the boot device
			;Bit 1 set if device is the boot console
; If flags bit 7 set:
02	DW	error	;Error message
04	DW	error	;Secondary error message
; For a block device (floppy / Winchester):
02	DB	sectors / track
03	DB	heads
04	DW	offset to start of partition
06	DB	read/write gap
07	DB	physical sector shift (0 => 128 bytes, 1 => 256 bytes etc.)	
; For the keyboard:
02	DB	keyboard type
; For a NEWDEV device:
04	DW	offset to driver ROM header (relative to boot ROM code segment)

Device ROMs

Additional character or block devices can be added to the boot environment. The boot ROM contains drivers for two of these — one for a video card (class 13h / 17h) and one for a 'Local Com Option' (class 38h / 78h). Other cards can provide these by means of an add-on ROM; if they do, the device class read from port xxFE must be in the range 50h-5Fh.

For add-on cards, the boot ROM writes 0F5h to xxFAh, making the device ROM visible at 0F4000h. The boot ROM then copies data from the card ROM to RAM. The ROM is paged out by writing 0F4h to xxFAh.

If bit 5 of the device flag byte is 0, the boot ROM assumes the device ROM is stored at even addresses only (so the first three bytes are at F400:0, F400:2 and F400:4).

The ROM begins with a header. The boot ROM populates the segments of DWORD addresses, bytes 1E-1F and 22-2A.

0000	DW	length of driver, bytes
0002	DW	length of RAM required, bytes
0004	DW	version
0006	DD	device selftest routine
000A	DD	device reset routine
000E	DD	?
0012	DD	device set boot geometry (cf INT 92h)
0016	DD	device load boot track (cf INT 91h)
001A	DD	character output
001E	DB	slot number
001F	DB	CRTC initialisation flag
0020	DW	offset of device name
0022	DW	segment of boot ROM
0024	DW	0
0026	DW	offset of 'thick' font in boot ROM 
0028	DW	offset of 'thin' font in boot ROM
002A	DB	7Fh	;Length of fonts (in characters) minus 1

Video Cards

The boot ROM has support for three different video cards: Classes 10h, 11h / 15h and 13h / 17h.

Class 10h

This is a colour card, based around a graphical framebuffer. There are two resolutions: 40 × 25 in 16 colours (initialised by the BIOS) and 80 × 25 in 4 colours.

When paged in, the framebuffer is 64k at E000:0. Each line is 256 bytes, and there are 256 lines.

In 16-colour mode, the memory is treated as four planes, with the low 2 bits of a byte's address giving the plane. So the first pixel is defined by bit 7 of the bytes at E000:0000, E000:0001, E000:0002 and E000:0003. In 4-colour mode, the principle is the same but there are only two planes.

Maximum resolution in 16-colour mode would be 512×256, but in practice the CRTC is set up for 320×225 by the ROM BIOS, and 320×200 by MS-DOS. In 4-colour mode, MS-DOS programs the CRTC for a 640×225 screen.

The following I/O ports are provided by this card:

CRTC register select
CRTC register data
Bit 0 is set to page the video card's memory in, clear to page it out.
Bit 2 is set for 24×80, reset for 24×40.
Bit 3 is set to raise an interrupt (IRQ3) on vertical blanking.
Sets which row of video RAM is to be displayed at the top left-hand corner of the screen. The high byte is the first pixel row to display, plus 0EBh. The low byte is the leftmost column, divided by 2.
Bit 2: In vertical refresh.
Bit 3: VSYNC active
Bit 4: HSYNC active
xx40 - xx5E
The palette. These are word registers, each one if which is treated as four nibbles. The values written by the BIOS are either 0 or F for each nibble. Only the top bit of each nibble appears to be significant; going from high to low, they give I R G B values for each of the 16 possible colours.
Write to reset the interrupt flag.
Bits 6-0 are 10h. Bit 7 is set if an interrupt has been raised on IRQ3.

Class 11h / 15h (PM-001)

The PM-001 video card has a small edge-connector on the top edge. This appears to be for a PM-002B graphics daughterboard. The card class is 11h if the daughterboard is absent, 15h if it is present.

This card operates like an MDA, with display generated from a buffer containing character codes. Resolution is 800x300, with each character cell 10 pixels wide and 12 high.

When this card is paged in, its character / attribute buffer is at F000:0000; even-numbered bytes are attributes, odd-numbered bytes are characters. This is the reverse of the way the MDA does it.

The technical reference describes the attributes as: as:

Bit 0: Blink
Bit 1: Reverse video
Bit 2: Blank -- character renders as blank
Bit 3: Bold (bright)
Bit 4: Overscore
Bit 5: Underscore
Bit 6: Subscript: Moves character down by one line
Bit 7: Superscript: Moves character up by one line.

The character bitmaps are stored at F200:0000, with 16 little-endian words per character. The low 10 bits of each word are used.

If the graphics daughterboard is present, it is mapped at E000:0000.

The following I/O ports are provided by this card:

CRTC register select
CRTC register data
Bit 0 is set to page the video card's memory in, clear to page it out.
Bit 3 is set to generate an IRQ3 interrupt on VSYNC.
The boot ROM seems to use this interchangeably with xx10.
Bits 6-0 return 11h or 15h, depending whether the graphics daughterboard is connected. Bit 7 is set if an IRQ3 interrupt is pending; reading the port clears the interrupt.

Class 13h / 17h

This card uses two NEC uPD7220 Graphic Display Controllers — one for text, one for graphics. I/O ports include:

xx00 - xx1E
Attribute table
Read: Text GDC status register
	Bit 1 set: Processor busy
	Bit 2 set: Command completed?
	Bit 5 set: Vertical blanking interval.
Write: Parameter into FIFO
Read: Read data from FIFO
Write: Command into FIFO
As xx20, for graphics GDC
As xx22, for graphics GDC
Underline position
Set DMA channel
Bit 0: Set to give DMA to the graphics GDC, clear for the text GDC
Bit 1: Set for DMA1
Bit 2: Set for DMA2
Bit 3: Set for DMA3
Bit 4: If set, card ID is 17h. If clear, 13h.
Bits 6-0 return 13h or 17h, depending on bit 4 of the value written to port xx2A.

Winchester drives

The first cylinder of a Winchester drive is reserved for system purposes, and has to be accessed using different commands at the controller level (F4 / F5 rather than C0 / C1). It is laid out as follows:

Sector 0: Configuration

00000000   57 72 69 74  65 20 70 72  6F 74 65 63  74 65 64 20  Write protected
00000010   3F 20 4E 6F  20 20 00 50  61 73 73 57  6F 72 64 20  ? No  .PassWord
00000020   3D 20 00 00  00 00 00 00  00 00 00 00  00 00 00 00  = ..............

If there is valid configuration information, the byte at 12h should be 'Y'. If so, the byte at 1Ch holds the flags: Bit 0 set for software read-only, bit 1 for password (at offset 22h).

Sector 1: Partition table

00000000   01 00 80 00  00 00 00 00  00 00 01 00  00 00 00 00  ................
00000010   00 00 00 00  00 00 00 00  00 00 00 00  00 41 01 09  .............A..
00000020   A0 00 00 00  00 00 00 00  00 01 00 00  00 00 00 00  ................
00000030   00 00 00 00  00 00 00 00  00 00 00 00  00 41 01 09  .............A..
00000040   50 00 00 00  00 00 00 00  00 01 00 00  00 00 00 00  P...............
00000050   00 00 00 00  00 00 00 00  00 00 00 00  00 41 01 09  .............A..
00000060   50 00 00 00  00 00 00 00  00 01 00 00  00 00 00 00  P...............
00000070   00 00 00 00  00 00 00 00  00 00 00 00  00 41 01 09  .............A..
00000080   50 00 00 00  00 00 00 00  00 01 00 00  00 00 00 00  P...............
00000090   00 00 00 00  00 00 00 00  00 00 00 00  00 41 01 09  .............A..
000000A0   50 00 00 00  00 00 00 00  00 01 00 00  00 00 00 00  P...............
000000B0   00 00 00 00  00 00 00 00  00 00 00 00  00 41 01 09  .............A..
000000C0   1D 00 00 00  00 00 00 00  00 01 00 00  00 00 00 00  ................
000000D0   00 00 00 00  00 00 00 00  00 00 00 00  00 41 01 09  .............A..
000000E0   00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  ................
000000F0   00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  ................

The partition table has up to 16 entries, each 32 bytes. The following bytes in an entry appear to have meaning:

00	DW	number of cylinders in this partition. 0 means end of table.
01	DB	flags:
			Bit 7 set if partition hidden
			Bit 1 set if partition format-protected
			Bit 0 set if partition write-protected
08	DW	Partition type: 
			0=Generic, exact type is given by class.
			1=Wang MSDOS
09	DW	Class:
			00000h: Normal
			00001h: Boot
			00100h: Wang / IBM
			080FFh: Reserved
			0FFFFh: System 
1D	DW 	0141h	; Initialised using BIOS 01.65
1F	DB	09	; Initialised using firmware 09.00

If the first entry begins with zero, the drive is not partitioned, and should contain a single filesystem.

Other system track sectors

Sector 2 is blank. Sector 3 contains information about the drive:

00000000   57 35 31 31  30 30 30 31  30 31 35 00  50 41 52 54  W5110001015.PART
00000010   49 54 49 4F  4E 45 44 3D  59 45 53 07  00 00 00 00  ITIONED=YES.....
00000020   EA FE 01 08  10 00 02 00  00 00 00 00  00 00 00 00  ................
00000030   00 00 00 00  00 00 00 00  00 00 00 80  00 41 01 09  .............A..
00000040   80 00 00 20  00 00 10 00  00 00 01 00  00 00 00 00  ... ............
00000050   00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  ................
00000060   C1 07 01 01  00 00 0A 0F  32 2E 30 31  2E 31 30 20  ........2.01.10
00000070   41 01 09 00  00 00 06 00  49 4E 49 54  57 00 00 00  A.......INITW...
00000080   00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  ................
00000090   00 00 00 00  00 00 00 00  00 00 00 00  00 00 00 00  ................
000000A0   C5 07 04 07  12 00 27 02  32 2E 30 30  20 20 20 20  ......'.2.00
000000B0   41 01 09 00  00 00 00 00  00 00 00 00  00 00 00 00  A...............
000000C0   C1 07 01 01  14 0B 51 39  32 2E 30 30  20 20 20 20  ......Q92.00
000000D0   41 01 09 00  00 00 00 00  00 00 00 00  00 00 00 00  A...............

The value at 00h-0Bh is the drive name. The 'PARTITIONED=YES' indicator is followed by 7, which is the number of partitions on the drive. If the drive is not partitioned (ie, it is a single volume) it reads 'PARTITIONED=NO ' and is followed by a zero.

The bytes at 20h are as follows:

20 	DB	partition control byte, 0 if drive is not partitioned.
21	DW	cylinders, not including reserved cylinder.
		In the example above, the drive has 512 cylinders, but the
		size reported is 510. The reserved cylinder is deducted,
		and the innermost cylinder is used as a landing zone.
23	DB	heads
24	DB	sectors
26	DW	sector size in bytes
28		zeroes
3B	DW	number of sectors per cylinder
3D	DW	BIOS version, as in the partition table
3F	DB	Firmware version, as in the partition table
40	D3 	start of data area (ie, sectors in this reserved cylinder)
43	D3	start of vendor track (assumed to be 1 track long, and only 
		exists if drive has more than 2 heads)
46	D3	start of defects track (assumed to be 1 track long)
49	DW	sectors per cylinder * 2

The 32 bytes at 60h are written by the format utility:

	00	dw	year		;Date stamp
	02	db	month
	03	db	day
	04	db	minute
	05	db	hour
	06	db	centisecond
	07	db	second
	08	db	format utility version (8 characters)
	10	dw	BIOS version, as at 3Dh
	12	db	firmware version, as at 3Dh
	16	dw	options used by format program (bit flags)
				Bit 0:  -Z, ignore bad system tracks
				Bit 1:  -W, confirm formatting Winchester drive
				Bit 2:  -R
				Bit 3:  -Q
				Bit 4:  -P
				Bit 5:  -N, ignore errors in vendor defect map
				Bit 6:  -M, load vendor defect map from file
				Bit 7:  
				Bit 8:  -F, save vendor defect map to file
				Bit 9:  
				Bit 10: -B
				Bit 11: -D, Input device name 
				Bit 12: -?
				Bit 13: -S, override hardware read-only flag?
				Bit 14: -J, blank the reserved cylinder
				Bit 15: -V, no volume label
	18	db	format program name	

The partitioning utility PDISKDEF writes a very similar structure (less the program name) at offset 0A0h. The example given has a third structure at 0C0h, presumably generated by another disk utility.

If the drive was formatted with the -F or -D options, there will be a device name at offset 0100h of this sector.

What I believe to be the list of bad blocks is stored at Cylinder 0, head 1, sector 0. Its format is unknown:

00000000   03 00 87 83  00 23 8A 00  4F DF 00 00  00 00 00 00  .....#..O.......

If the drive has more than two heads, then a vendor defect map is stored at Cylinder 0, head 2, sector 0:

00004000   56 45 4E 4D  41 50 00 00  00 00 00 00  00 00 00 00  VENMAP..........

The data area follows the system track. On a bootable drive it should begin with a standard Wang-format boot sector — either the volume boot sector, if the drive is not partitioned, or the master boot record if it is.

Winchester cards

The Winchester card is based on a Z80 processor, with a 4k ROM containing firmware. Unlike the Winchester card on an IBM XT or an Apricot PC, it only supports one drive, not two; two drives would require two cards in two slots.

I/O ports are:

Main status register.
	Bit 0 set if the controller is busy.
	Bit 1 set if the controller failed self-test.
	Bit 2 set if controller has result bytes to return (through the data
	Bit 3 set if the Z80 in the controller received a non-maskable
	     interrupt. The technical guide specifies that 5Mb drives set
             this bit to 0, all others to 1.
	Bits 7-4 are a counter that increments with each byte written to the
	     controller. The boot ROM requires this to be on 0A0h during
	     its self-test.
	Bit 8 is checked by the IRQ2 service routine in MS-DOS; if it is
	     not set and the controller is raising an interrupt, the 
             controller is reset.

	Immediately after a DMA transfer, the boot ROM requires a read of the
	main status register to return a count that increments with each DMA 
Data register. Used to send commands and retrieve results.
The boot ROM reads this (and ignores the result) when handling a hard drive interrupt, presumably to acknowledge the interrupt.
Select DMA channel. The boot ROM writes 00h for none, 02h for DMA1, 04h for DMA2 or 08h for DMA3. This appears also to select the IRQ the Winchester interrupts on: 02h for IRQ5, 04h for IRQ6, 08h for IRQ7.
Reset the controller.
When read, returns the device class (always 1) in the bottom 7 bits. The top bit is 1 if the device is interrupting. When written, appears to select DMA channel as for xx06.

All commands to the Winchester (except 0FFh, which is used in the register self-test) are 8 bytes long. For each byte to send, read the main status register until bit 0 is 0; then write the value to the data register. Once the eight bytes have been sent, wait for an interrupt on the Winchester's interrupt line (5, 6 or 7). Clear the interrupt by reading port xx04, then read result bytes until bit 2 of the main status register goes to 0. There will normally be 8 result bytes.

The normal format of a command packet is:

	DB command
	DB 0		;Not normally used on input
	DW cylinder	;Cylinder to read/write, 0-based.
	DB head		;Head, 0-based
	DB sector	;Sector, 0-based
	DW sec_count	;Count of sectors to transfer

Note that the normal C0 and C1 commands internally add 1 to the cylinder number, so they cannot be used to access the system track.

The 8-byte result packet will then be:

	DB command
	DB result	;80h for OK, 1-7Fh for error. Errors are:
			;02h: Write protected
			;03h: CRC error
			;04h: Sector not found
			;05h: Verify failed: data did not match
			;08h: Unknown command
			;09h: Drive fault
	DW cylinder	;The next cylinder / head / sector to transfer
	DB head		
	DB sector
	DW sec_count	;Count of sectors actually transferred

Command bytes understood by the Winchester:

44: Internal read sector command.
C0: Read sector(s).
C1: Write sector(s).
C2: Verify sector(s). Checks that the sectors can be loaded into controller
    RAM, but does not do any DMA transfer to/from main memory.
C3: Format track. Only cylinder and head parameters are used.
C9: Equivalent to C1 followed by C0: Write then reread, using the same sector 
    parameters for both operations.
F0: Reset the controller.
F1: Reset retry count.
F2: Return the retry count in the sec_count record.
F3: Format the entire drive. Initialise the system track to 0 and populate
    sector 0. Bytes 3-6 of the command are month (byte), day (byte) and 
    year (word).
F4: Read sector(s) from the system track. As C0, except the cylinder number is
	* The technical guide calls this 'Read Error Data', but this does
	 not match the behaviour of actual software.
F5: Write sector(s) to the system track. As C1, except that the cylinder 
   number is ignored.
	* The technical guide calls this 'Read Reserved Cylinder', but this 
	 does not match the behaviour of actual software.
F6: Set drive read-only and password settings (saved in sector 0 of the
	system track).
	Byte 1 of command is 0 for none, 1 for read-only, 2 for password,
	     3 for both.
	Bytes 2-7 of command are password. 
	* The technical guide calls this 'Write Enable'.
F7: The technical guide calls this 'Write Protect'. Takes password in 
   bytes 2-7 of command.
F8: Returns two ECC counters, at offsets 4 and 6 of the results block.
F9: Reset the two ECC counters.
FA: Returns 700h bytes through the DMA, from 1000h-16FFh in the Z80's 
   address space.
FB: Load custom firmware. Loads 700h bytes through the DMA, and executes
   them in the Z80's address space at 1000h.
FC: Read drive geometry. 8-byte command.
	Byte 1: 80h if drive does not support hardware readonly, 81h if 
		it does.
	Byte 2,3: Max cylinder
	Byte 4:   Max head
	Byte 5:   Bit 7-5 Drive hardware type, specified in a table in the 
                  controller firmware:
				ID cylinders heads  rwc   wp
				 0   609     2      128   1
                                 1   611     4        0   0
                                 2  1023     8     1025   0
                                 5   511     8      256   0 
                                 6   644     7      256   0
                                 7   305     4      128   0
		  I have assumed that 'rwc' is a reduced-write-current 
		  cylinder. 'wp' is 1 if the drive support hardware write
		  Bit 4 status of hardware write protect.
		  Bit 3 always 1
		  Bit 2 always 0
		  Bit 1 password protected	
		  Bit 0 software read-only
	Byte 6:   Firmware version, minor
	Byte 7:   Firmware version, major

FD: One parameter: a byte at command+2. Possibly retry count?
FE: Enter register test mode.
FF: Enter register test mode.

Other commands are invalid and will generate an 8-byte error packet:
00 08 00 00 00 00 00 00 

Memory Expansion Card

This is based on disassembly of the MS-DOS self-test procedure, rather than the study of real hardware.

The memory card (class 3Fh) contains 1-4 banks of memory, 128k in size, which can be mapped into the processor's address space. This memory is paged in or out by a word write to port xxC0h.

Bit 15:     Page in bank 4
Bits 14-12: Base address of bank 4, divided by 128k
Bit 11:     Page in bank 3
Bits 10-8:  Base address of bank 3, divided by 128k
Bit 7:      Page in bank 2
Bits 6-4:   Base address of bank 2, divided by 128k
Bit 3:      Page in bank 1
Bits 2-0:   Base address of bank 1, divided by 128k

John Elliott 3 January 2012.