I have been working on an emulator for early (Motorola 68000-powered) Macintosh computers. While implementing the disk drive, I noticed documentation was scattered and hard to find. Now that I have a working implementation, this post is my attempt to document everything in one place.

Floppy with question mark

IWM: the disk drive controller

The 68000 CPU interfaces with the disk drive through a controller, called ‘IWM’ (Integrated Woz Machine, a reference to Apple co-founder Steve Wozniak, who designed it). This controller handles all the logic and analog signals to the drive itself and exposes them in a usable way to the CPU.

The IWM controller was originally designed for 5.25” drives for the Apple II but the implementation has been modernized for 3.5” drives and the Macintosh. Many elements are, however, still the same and will be familiar to those familiar with the Apple II. For example, the CA0/CA1/CA2 I/O lines were used in the Apple II to move the drive head, but have been repurposed for register access in the 3.5”/Macintosh version.

From the Macintosh Plus onwards, it can control up to two disk drives.

I/O

All IWM I/O lines are memory-mapped from the CPUs perspective. There are addresses to set and clear certain signals and read/write bytes from and to the controller. The table below outlines the addresses, their associated I/O line, their purpose and whether they enable or disable the line.

Note that the ‘HEADSEL’ line is not directly memory mapped but has to be toggled through the VIA (Register A, bit 5).

Line Enable address Disable address Purpose
CA0 0xDFE3FF 0xDFE1FF Drive register selection
CA1 0xDFE7FF 0xDFE5FF Drive register selection
CA2 0xDFEBFF 0xDFE9FF Drive register selection
LSTRB 0xDFEFFF 0xDFEDFF Drive register write strobe
ENABLE 0xDFF3FF 0xDFF1FF Drive enable
Required for many drive commands.
SELECT 0xDFF7FF 0xDFF5FF Drive select
Selects internal (off) or external (on) disk drive.
Note that the Mac 128K/512K can only address one drive, due to the PWM speed control.
Q6 0xDFFBFF 0xDFF9FF Selects which IWM register to access
Q7 0xDFFFFF 0xDFFDFF Selects which IWM register to access
HEADSEL Through VIA Through VIA Two purposes: selects the drive head to read/write data from either side of the disk (in double-sided drives) and plays part in drive register selection.

Note that either reading or writing any of these locations will affect the associated I/O line.

The Macintosh ROM will only ever access these addresses as bytes. Only the lower eight data lines of the 68000’s data bus are connected to the IWM, meaning it should only be accessed on byte addresses with the lowest address bit set.

In the event a less well behaved application attempts access to an even address, it is worth noting that:

  • The 68000’s UDS and LDS lines are not connected to the IWM, meaning any byte-sized write will go through as if bit 0 is ignored for even addresses because the CPU outputs the byte on the lower as well as the upper eight data lines.
  • Reads to even addresses will be undefined as the upper 8 data lines are not connected to the IWM.

IWM registers

Note the differentiation above between IWM and drive registers. The IWM registers are part of the controller. The drive registers are per-drive, however, they are queried through the IWM status register, as will be described later on.

The ENABLE, Q6 and Q7 lines together select which IWM register to write or query:

ENABLE Q6 Q7 Read/Write Register addressed
on off off R Data register (reading track)
on on on W Data register (writing track)
on on off R Status register
on off on R Handshake register
off on off W Mode register

Mode register

The mode register configures the operation of the IWM and is shared among all drives. Although this register is write-only, it can be read back as part of the status register.

The Macintosh will normally always set the mode to $1F during very early boot and never change it during normal operation. However, it is known that some applications may change the mode register as part of disk copy protection (e.g. by changing the IWM clock speed).

To change the mode register, software would:

  1. Read the status register to confirm the mode needs changing,
  2. Access ‘ENABLE off’ (0xDFF1FF),
  3. Access ‘Q6 on’ (0xDFFBFF),
  4. Write the desired value to ‘Q7 on’ (0xDFFFFF),
  5. Read the status register to confirm the mode has changed. If not, start over.

The bits in the mode register are described below.

Bit(s) Description
7-5 Reserved/irrelevant
4 Clock speed
0 = 7 MHz, 1 = 8 MHz.
3 Bit cell timing
0 = slow (4 µs/bit), 1 = fast (2 µs/bit)
2 Spindle motor shutdown delay
0 = one second, 1 = none
1 Write handshake protocol
0 = synchronous, 1 = asynchronous
0 Data latch mode
0 = no latching, 1 = data latched on most significant bit set

Status register and drive registers

The status register reports a few general controller status bits but also allows for reading 1-bit disk drive registers. To read the status register, software would do the following:

  1. Set ‘SELECT’ according to the drive to be queried,
  2. Set the CA0, CA1, CA2 and HEADSEL to select the desired drive register,
  3. Access ‘ENABLE on’ (0xDFF3FF),
  4. Access ‘Q6 on’ (0xDFFBFF),
  5. Read the status byte from ‘Q7 off’ (0xDFFDFF).

The table below describes the bits in the status byte:

Bit(s) Description
7 SENSE
The content of the drive register that was inquired
6 Reserved
5 ENABLE line status. 0 = enable off, 1 = enable on.
4-0 Same as bit 4-0 of the mode register.

The table below shows a list of drive registers that can be queried:

CA2 CA1 CA0 HEADSEL Register Description
off off off off DIRTN Head step direction
0 = track up, 1 = track down
off off off on CISTN Disk inserted
0 = disk in drive, 1 = no disk
off off on off STEP Head stepping in progress
0 = head stepping, 1 = head still
off off on on WRTPRT Disk write protect status
0 = write protected, 1 = not write protected
off on off off MOTORON Spindle motor running
0 = running, 1 = off
off on off on TKO Head at track 0
0 = track 0, 1 = other track
off on on on TACH Spindle motor tachometer
60 pulses/disk revolution (= 120 edges)
on off off off RDDATA0 Live track data (1-bit), lower head
on off off on RDDATA1 Live track data (1-bit), upper head
on on off off SIDES Drive head count
0 = one head (single sided, 400K drive), 1 = two heads (double sided, 800K drive)
on on off on READY(?) Disk ready (? unsure if correct)
0 = ready, 1 = not ready
on on on off INSTALLED Disk drive installed
0 = installed, 1 = not installed
on on on on PRESENT/HD(?) Disk drive installed (? unsure if correct)
0 = installed, 1 = not installed

Drive commands / writing registers

Disk drives can also receive commands through a similar selection mechanism as above.

  1. Set ‘SELECT’ according to the drive to be commanded,
  2. Set the CA0, CA1, CA2 and HEADSEL to select the desired drive command,
  3. Access ‘ENABLE on’ (0xDFF3FF),
  4. Access ‘LSTRB on’ (0xDFEFFF),
  5. Access ‘LSTRB off’ (0xDFEDFF) after a minimum of 1 µs (except EJECT).

The table below shows the possible drive commands:

CA2 CA1 CA0 HEADSEL Command Description
off off off off TRACKUP Sets head movement direction to higher tracks
on off off off TRACKDN Sets head movement direction to lower tracks
off off on off TRACKSTEP Moves the drive head one track in the selected direction. A step may take up to 30 ms for the head to settle. If the destination track is in a different speed group, it can take up to 150 ms for the motor to reach the proper speed.
off on off off MOTORON Enables the spindle motor. Note that the spindle motor will not actually run unless there is a disk in the drive. The motor will be running at the proper speed within 400 ms.
on on off off MOTOROFF Stops the spindle motor.
on on on off EJECT Ejects the disk. Note that the LSTRB signal must remain active for 750ms for the eject to occur.*

*: the Macintosh Plus briefly asserts eject during boot for unknown reasons. This does not cause an actual eject on hardware, but re-emphasizes the need to observe the 750ms in an emulator.

Handshake register

The handshake register is used when writing data to the disk to synchronize the write timing between the IWM and the CPU. See ‘Write data register’ below on how the disk writing process works. To read the handshake register, software would do the following:

  1. Set ‘SELECT’ according to the drive to be queried,
  2. Access ‘ENABLE on’ (0xDFF3FF),
  3. Access ‘Q7 on’ (0xDFFFFF),
  4. Read the handshake byte from ‘Q6 off’ (0xDFF9FF).

The table below describes the bits in the handshake byte:

Bit(s) Description
7 Ready for data (data register empty)
0 - not ready, 1 - ready
6 Buffer underrun (write completed)
0 - underrun occurred, 1 - no underrun
5-0 Reserved

In an idle situation, where the disk is not being written, ‘ready for data’ is set and ‘underrun’ is clear.

Read data register

In latch mode (always enabled on Mac), while the drive motor is running, every bit that passes under the head is left-shifted into a shift register. When the IWM sees the most significant bit in this register is set, the contents are copied into the data register and the shift register is cleared. This mechanism works based on the fact that, in GCR encoding, any valid byte has the most significant bit set. If there was already a byte in the data register, that byte is lost.

Reading the data register clears it to 0. The data read from this register is raw, encoded track data. Decoding the bytes, locating the right sector, etc. is all done in software. To read the data register, software would:

  1. Set ‘SELECT’ according to the drive to be queried,
  2. Access ‘ENABLE on’ (0xDFF3FF),
  3. Access ‘Q7 off’ (0xDFFDFF),
  4. Read the data byte from ‘Q6 off’ (0xDFF9FF).

Write data register

Writing to the write data register makes the drive write to the disk. The data register is a buffer for the write shift register. When the write shift register is empty, the content of the data register is copied to the shift register. From this point, the data register can be filled again.

As the drive head progresses over the track, bits from the shift register are shifted out (from the most significant bit) and written to the disk.

There are two flags in the handshake register that are used to keep the buffer filled and synchronize the write process:

  • Ready for data: this flag is set as soon as the data register content is moved to the shift register, signalling that the write data register needs to be filled with the next byte.
  • Underrun: if the shift register is empty and there is no new byte waiting in the data register, writing stops and the underrun flag is set.

Software would follow the following process to write to disk:

  1. Select the desired drive, enable drive motor, move to desired track,
  2. Wait for the correct track position to line up under the head,
  3. Write the next byte to write to the data register:
    1. Access ‘Q7 on’ (0xDFFFFF),
    2. Write the desired value to ‘Q6 on’ (0xDFFBFF),
  4. If there is data left to be written, wait for the ‘ready for data’ bit to be set, then return to step 3.
  5. If there is no data left, wait for underrun.

The disk format

Macintosh floppies use Group Coded Recording (GCR) encoding in 6-and-2 encoding. Tracks are divided in sectors. Every sector can store an effective number of 512 (logical) bytes.

Sectors contain headers called ‘address markers’. These markers are used to locate specific sectors on the track. They contain:

  • A preamble pattern used to locate the start of a header,
  • The sector number,
  • The track number and disk side,
  • The disk format - $02 for single-sided 400K disks, $22 for double-sided 800K disks,
  • A checksum of the header,
  • A few trailing bytes.

Drive speed and timing

Macintosh disk drives spin at a Constant Linear Velocity, meaning the speed in which the disk spins increases towards the inside of the disk. By reading the outer tracks at a lower speed, it was possible to store data bits more densely packed together. This allowed Macintosh computers to store 400KB on a single-sided diskette or 800K on a double-sided diskette, where a PC would only manage to store 360KB or 720KB of data.

The disadvantage of this technique was that there was no interoperability with PC diskettes possible, at all, even with software changes. For the Macintosh, this changed with the introduction of the ‘SuperDrive’ or ‘FDHD’ in 1988. First offered as an upgrade on the Macintosh II and Macintosh SE, the SuperDrive was composed of a new ‘SWIM’ (Super Woz Integrated Machine) disk drive controller IC and a new disk drive which could read and write both Macintosh CLV and PC CAV formatted diskettes. The Macintosh IIx, introduced in September 1988, was the first model to include the SuperDrive as standard.

Below, there’s a table which shows the varying drive speeds per track and the amount of physical (e.g. encoded) bits per track.

Track range Drive speed Physical bits per track
0 - 15 ~402 rpm 74640
16 - 31 ~438 rpm 68240
32 - 47 ~482 rpm 62200
48 - 63 ~536 rpm 55980
64 - 79 ~603 rpm 49760

The approximate amount of CPU cycles per disk bit can be calculated using:

clock_speed * 60 / track_rpm / bits_per_track

At 8 MHz, the drive head produces a new bit approximately every 16 cycles, regardless of the track.

Speed control in the early Macintosh

In the Macintosh 128K and 512K with the 400K disk drives, the speed of the spindle motor is controlled by software using a PWM signal that is written to the LSB of every 16-bit slot in the sound buffer.

At boot, the Macintosh ROM performs an initial calibration by setting the drive to two different speeds and reading back the tachometer signal (TACH register). This calibration deals with temperature and component variance in the drive and electronics. During system operation, the drive is frequently re-calibrated.

To calculate the PWM duty cycle from the data in the sound buffer, convert the lower 6 bits of each value using the following table (calculation from MAME):

 0,  1, 59,  2, 60, 40, 54,  3,
61, 32, 49, 41, 55, 19, 35,  4,
62, 52, 30, 33, 50, 12, 14, 42,
56, 16, 27, 20, 36, 23, 44,  5,
63, 58, 39, 53, 31, 48, 18, 34,
51, 29, 11, 13, 15, 26, 22, 43,
57, 38, 47, 17, 28, 10, 25, 21,
37, 46,  9, 24, 45,  8,  7,  6

Sum these over a period of 100 values, then calculate the index using:

    sum / (count / 10) - 11

Clamp the resulting value to 0-399 and calculate the duty cycle percentage by dividing the result by 4.19.

The disk drive specifications state the following bounds for drive speed vs. duty cycle:

9.4% 305 < V < 380 rpm
91% 625 < V < 780 rpm

With this information, you can calculate the drive speed dictated by the driver in the Macintosh ROM.

Emulating the PWM speed control is required, the software will not initialize the disk drive successfully if it notices the drive does not respond to PWM changes during calibration. An improper implementation may simply lead to failure to read the boot disk but may also cause a ‘sad Mac’ with a division by zero error.

Sad Mac, division by zero

Speed control in the Macintosh Plus and onwards

On models with dual-sided (800K) drives and onwards, the drive speed is controlled by hardware. When emulating the IWM, as long as the speed per track group matches the table above, it should work.

The software side

The ROM contains a driver that talks to the IWM and disk drive, called ‘Sony’. The name likely chosen because Sony designed and manufactured the early Mac disk drives. This driver exposes the decoded sector data, upon which the filesystem layer is built.

The IWM supplies the CPU with the physical, encoded track data straight from the disk. Decoding the GCR-encoding, finding and parsing the sector headers, etc. is all handled by the driver software.

For emulation purposes, knowledge of the Sony driver is not strictly required. However, it helps tremendously when debugging issues.

ROM API functions

Below is a list of Sony driver functions exposed by the Macintosh 512K ROM and the purpose of the function I’ve been able to discover.

Function Purpose
E_Sony_Open Initializes driver, enumerates disk drives
E_Sony_Prime Sets up drive for reading/writing
E_Sony_Control Drive control commands (e.g. eject etc)
E_Sony_Status Drive status request
E_Sony_Close Does nothing?
P_Sony_FigTrkSpd Calibrates the drive speed using the tacho signal.
P_Sony_DiskPrime Sets up drive for reading/writing
P_Sony_RdAddr Reads and decodes an address mark
P_Sony_RdData Reads and decodes sector data
P_Sony_WrData Writes a sector
P_Sony_Seek Seeks to a sector
P_Sony_SetUpPoll See below (polling)
P_Sony_Recal Recalls the head to track 0
P_Sony_Control Drive control commands
P_Sony_WakeUp Yields the CPU to other tasks for some time
P_Sony_ReSeek Seeks to a sector
P_Sony_MakeSpdTbl Part of the drive calibration process

Polling the SCC

Reading/writing in the Sony driver occurs in a critical section where interrupts are masked. To avoid losing serial data during this time, the driver will poll the SCC write request pin and read the serial data onto a stack. If there are too many serial events during this critical section, that stack may overflow.

Boot sequence and errors

The ROM attempts to load a piece of boot code from the disk into RAM, to which it will jump.

Below are a few errors I have encountered which may help in troubleshooting. In any case, it is very helpful to debug the ROM and see which Sony driver functions it goes through until it fails. Note that negative return values from these functions indicate errors.

If you do not get the timing or IWM responses right in your emulator, you will likely get the crossed-out disk, which means that the ROM did not manage to load and execute the boot code from the disk:

Floppy with cross

If the ROM bootstrap does manage to load and execute the boot code, but the boot code does not manage to find the second stage system file, you will get an ‘Invalid system disk’ sad Mac, like below. In that case, you may not be feeding the right track data.

Bad system disk error

If everything works out, you should be greeted with the welcome screen, and finally, the desktop:

Welcome to Macintosh

Macintosh desktop

References