Analysis of stitched_cpm_227_608m_system_40ss.dsk, a clean CP/M 2.27 system
disk for the Spectravideo SVI-328 with SV-608M memory/hard-disk expansion.
The disk image was reconstructed by stitching the best tracks from two
independent dumps (dump5 and dump6) of the same physical floppy disk.
The system tracks (0–1) are byte-identical across both dumps; only 4 bytes
differed at track 16, sector 7, positions 252–255 (corrected from dump6).
| Parameter | Value |
|---|---|
| Sides | 1 (single-sided) |
| Tracks | 40 |
| Track 0 | 18 sectors × 128 bytes = 2,304 bytes |
| Tracks 1–39 | 17 sectors × 256 bytes = 4,352 bytes |
| Total size | 172,032 bytes |
The CP/M system occupies tracks 0 and 1:
| Component | Disk Location | Load Address | Size |
|---|---|---|---|
| Boot sector | Track 0, sector 1 | C100h | 128 bytes |
| BIOS | Track 0, sectors 2–18 | D600h | 5,248 bytes |
| + Track 1, sectors 1–12 | |||
| CCP + BDOS | Track 1, sectors 13–17 | C000h | 5,632 bytes |
| (+ next track) |
0000h–00FFh Zero page (BIOS vectors, system parameters)
C000h–C005h CCP entry points (CCP_ENTRY, CCP_WBOOT_ENTRY)
C000h–C805h CCP (Console Command Processor)
C806h–D5FFh BDOS (Basic Disk Operating System)
D600h–D630h BIOS jump table (17 entries)
DFB2h BOOT_impl — cold boot
E098h WBOOT_impl — warm boot
EEE7h–EF4Bh DMA buffer area
BOOT_impl (DFB2h): disables interrupts, initialises memory mapper,
timer, RST 38h vector, and function keysOUT (40h), 00h
(SUB39 at E227h — de-asserts all SASI bus signals)SUB09 at DC46h — probes port 50h/51h)SUB10 at DCB1h — ports 28h–2Dh)Spectravideo CP/M-80 Revision 2.27 For SV-608M Booted From Floppy DiskSUB32 (E09Bh): sets up zero-page vectors
(0000h → JP D603h, 0005h → JP C806h)SUB40 (E233h) → SUB47 (E449h) to send the WD1002-SHD
Set Parameters command via SASI. This is where the system hangs
without SV-608M hardware (see below).The SV-608M communicates with its hard disk via the SASI (Shugart Associates System Interface) bus protocol. The SASI disk controller is a Western Digital WD1002-SHD Winchester Disk Controller board.
Reference: WD1002-SHD OEM Manual (Aug 1984)
The WD1002-SHD is an intelligent controller board containing a WD1010 Winchester controller chip, an 8X300 microprocessor for SASI protocol handling, and a built-in data separator with ECC support.
The attached hard disk is a Seagate ST-212, a 10 MB, 5.25” half-height Winchester drive.
Reference: Seagate ST-212 Product Manual (Rev. B)
| Parameter | Value |
|---|---|
| Formatted capacity | 10.0 MB |
| Cylinders | 306 |
| Heads | 4 |
| Tracks | 1,224 (306 × 4) |
| Sectors per track | 32 |
| Bytes per sector | 256 |
| Bytes per track | 8,192 |
| Recording method | MFM |
| Data transfer rate | 5.0 Mbit/s |
| Discs | 2 |
| Track-to-track seek | 23 ms max |
| Average seek | 65 ms max |
BIOS vs physical capacity: The BIOS partitions use 1,160 of the 1,224 available tracks (290 of 306 cylinders), leaving 64 tracks (16 cylinders) at the inner edge unused — likely reserved for bad track alternates, which the WD1002-SHD supports via its Format Alternate Track command (opcode 0Eh).
The SV-608M card provides a register-mapped interface to the raw SASI bus signals. The BIOS accesses two separate port groups:
| Port | R/W | SASI Function |
|---|---|---|
| 40h | W | Bus control (reset — writing 00h de-asserts all signals) |
| 41h | W | Data bus output (host → bus), auto-generates ACK handshake |
| 42h | R | Data bus input (bus → host), auto-generates ACK handshake |
| 43h | W | SEL assertion (triggers Target Selection Phase) |
| 44h | ? | Additional control (not used in normal read/write) |
| 46h | R | SASI bus status — reads 5 control signals as a 5-bit value |
The status register at port 46h directly exposes the 5 SASI bus control
signals. The BIOS reads this register and masks with AND 1Fh to get the
current bus phase:
| Bit | Signal | Name |
|---|---|---|
| 0 | BSY | Busy |
| 1 | REQ | Request |
| 2 | C/D | Command/Data |
| 3 | I/O | Input/Output |
| 4 | MSG | Message |
This maps directly to the Information Transfer Phase Configurations table from the WD1002-SHD manual (Table 6-1):
| Status | Binary | BSY | REQ | C/D | I/O | MSG | SASI Phase | Direction |
|---|---|---|---|---|---|---|---|---|
| 00h | 00000 | 0 | 0 | 0 | 0 | 0 | Bus Free | — |
| 03h | 00011 | 1 | 1 | 0 | 0 | 0 | Data Out | Host → Controller |
| 0Bh | 01011 | 1 | 1 | 1 | 0 | 0 | Command | Host → Controller |
| 13h | 10011 | 1 | 1 | 0 | 1 | 0 | Data In | Controller → Host |
| 1Bh | 11011 | 1 | 1 | 1 | 1 | 0 | Status | Controller → Host |
| 1Fh | 11111 | 1 | 1 | 1 | 1 | 1 | Message In | Controller → Host |
Every status value polled by the BIOS now has a precise meaning in the standard SASI bus protocol.
A complete SASI transaction, as performed by the BIOS, follows this sequence:
SUB53: Poll until (46h & 1Fh) == 00hSUB50: Poll for 0Bh, then write each command
byte to port 41h. The 6-byte SASI Group 0 command block is sent.SUB43: Poll for 13h, then INIR 256 bytes
from port 42h. (For writes: Data Out Phase, 03h, OUTIR to port 41h.)SUB51: Poll for 1Bh, read 1-byte status from
port 42h. Bit 1 = error flag.SUB52: Poll for 1Fh, read 1-byte completion
(always 00h, signals bus freed).Ports 60h–63h are a Centronics parallel printer interface on the SV-608M expansion unit, also used for hardware presence detection:
SUB55 (E506h): Initialises the printer interface — clears strobe,
aux and status ports, sets data port to FFh (idle), then writes 34h to
aux and strobeSUB56 (E5A7h): Detects SV-608M presence — toggles bit 2 of AUX
port (63h) and reads STATUS port (62h) before and after. If STATUS
changes, the hardware is present (carry clear). If same, no hardware
(carry set).At boot, SUB59 (E8A6h) calls SUB55 and SUB56, then routes the CP/M
LIST device:
This BIOS unconditionally attempts to communicate with the SV-608M
SASI adapter at boot time. The controller init has no timeout or fallback —
SUB53 at E4DAh busy-waits forever for the Bus Free phase (port 46h &
1Fh == 0).
Without SV-608M hardware (or something that responds on port 46h), the system hangs immediately after printing the boot banner, stuck in the infinite poll loop at E4DAh.
SELDSK_impl (DCECh) supports 5 drives (0–4, mapped to A:–E:). Each Disk
Parameter Header (DPH) is 16 bytes starting at D739h. The DPH points to a
Disk Parameter Block (DPB) that defines all geometry. All geometry is
hardcoded in the DPBs — no runtime query of the disk’s physical geometry
is performed.
Both the floppy BIOS and the HDD BIOS support only one floppy drive. The SVI-328 hardware can physically connect two floppy drives, but this CP/M 2.27 + 608M BIOS only provisions a single floppy in both builds.
The floppy system disk and T0T1.SYS (on the HDD) contain different BIOS builds with different drive letter assignments. Both use the same 5 DPBs (D7A7h, D7C6h, D7D5h, D7E4h, D7F3h) but assign them to different drives:
| Drive | DPH | DPB | SPT | BSH | Block | DSM | DRM | CKS | OFF | Capacity | Type |
|---|---|---|---|---|---|---|---|---|---|---|---|
| A: | D739h | D7A7h | 34 | 4 | 2K | 162 | 63 | 16 | 3 | 334K | Floppy (80-track) |
| B: | D749h | D7C6h | 64 | 4 | 2K | 1023 | 383 | 0 | 2 | 2,048K | HDD partition 1 |
| C: | D759h | D7D5h | 64 | 4 | 2K | 1023 | 383 | 0 | 258 | 2,048K | HDD partition 2 |
| D: | D769h | D7E4h | 64 | 4 | 2K | 1023 | 383 | 0 | 514 | 2,048K | HDD partition 3 |
| E: | D779h | D7F3h† | 64 | 4 | 2K | 1559 | 511 | 0 | 770 | 3,120K | HDD partition 4 |
| Drive | DPH | DPB | SPT | BSH | Block | DSM | DRM | CKS | OFF | Capacity | Type |
|---|---|---|---|---|---|---|---|---|---|---|---|
| A: | D739h | D7C6h | 64 | 4 | 2K | 1023 | 383 | 0 | 2 | 2,048K | HDD partition 1 |
| B: | D749h | D7D5h | 64 | 4 | 2K | 1023 | 383 | 0 | 258 | 2,048K | HDD partition 2 |
| C: | D759h | D7E4h | 64 | 4 | 2K | 1023 | 383 | 0 | 514 | 2,048K | HDD partition 3 |
| D: | D769h | D7F3h | 64 | 4 | 2K | 1559 | 511 | 0 | 770 | 3,120K | HDD partition 4 |
| E: | D779h | D7A7h | 34 | 4 | 2K | 162 | 63 | 16 | 3 | 326K | Floppy (80-track) |
The DPB pointers rotate: the floppy BIOS puts the floppy DPB (D7A7h) on drive A: and the four HDD DPBs on B:–E:; the HDD BIOS shifts the HDD DPBs to A:–D: and puts the floppy DPB on E:. This means files on HDD partition 1 appear as B: when booted from floppy, but as A: when booted from HDD.
Column definitions (standard CP/M 2.2 Disk Parameter Block fields):
Two additional DPBs exist for the floppy drive to support 40-track floppy formats. The subroutine at D7B6h toggles the DPB pointer in DPH D779h (drive E: in both builds). In the HDD BIOS, drive E: is the floppy, so this toggle selects between 40-track formats. In the floppy BIOS, drive E: defaults to HDD partition 4; the toggle’s intended use in that context is unclear. The subroutine at D7B6h switches this DPH to point to one of these:
| DPB | SPT | Block | DSM | Capacity | Format |
|---|---|---|---|---|---|
| D789h | 34 | 1K | 156 | 157K | 40-track SS, 17×256-byte |
| D798h | 32 | 1K | 131 | 132K | 40-track SS, 16×256-byte |
The toggle at D7B6h reads the current DPB pointer from the DPH, and if it points to D789h, switches to D798h, and vice versa.
The floppy DPB (D7A7h) has SPT=34 and DSM=162 with 2K blocks. The resulting capacity (163 × 2,048 = 333,824 bytes) requires 77 data tracks plus 3 system tracks (OFF=3) = 80 tracks total. This is consistent with an 80-track single-sided floppy drive, distinct from the standard SVI-328 40-track drive. This DPB is used for drive A: in the floppy BIOS, or drive E: in the HDD BIOS.
The four HDD partitions are laid out sequentially on the same hard disk, addressed by a flat track numbering scheme:
Track 0 1 2 257 258 513 514 769 770 1159
├─────┤ ├─────────┤ ├─────────┤ ├─────────┤ ├─────────┤
reserved Part 1, 2MB Part 2, 2MB Part 3, 2MB Part 4, ~3MB
(2 trks) (256 trks) (256 trks) (256 trks) (390 trks)
Drive letter assignment depends on which BIOS is running:
Total HDD capacity: 1,160 tracks × 8,192 bytes/track = 9,502,720 bytes (~9.05 MB) across 4 partitions.
Each HDD track contains 32 physical sectors of 256 bytes (= 64 CP/M logical 128-byte sectors), yielding 8,192 bytes per track.
The hard disk is accessed through the SV-608M SASI host adapter (ports 40h–46h). All commands follow the standard SASI bus phase sequence described above.
SUB36 (E15Bh, read) and SUB37 (E1B1h, write) convert the CP/M
track/sector to a 16-bit linear sector address:
linear_sector = track × 32 + sector − 1
This is computed using a multiply loop (ADD IX,DE repeated 32 times at
E18Ah/E1E0h), where DE = track number from SETTRK, and the
sector from SETSEC is added at the end.
SUB42 (E3A9h) builds a 6-byte SASI Group 0 command block at E4F9h:
| Offset | Value | SASI Field |
|---|---|---|
| 0 | 08h | Command class 0, opcode 08h (Read) |
| 1 | 00h | LUN=0 (bits 7-5), sector addr bits 20-16=0 (bits 4-0) |
| 2 | sector addr high | Logical sector address, bits 15-8 |
| 3 | sector addr low | Logical sector address, bits 7-0 |
| 4 | 01h | Block count = 1 sector |
| 5 | 47h | WD1002-SHD control byte (see below) |
Control byte 47h (01000111b) decoded per WD1002-SHD Table 4-2:
| Bits | Field | Value | Meaning |
|---|---|---|---|
| 7 | R | 0 | Retries enabled (up to 10 + restore) |
| 6 | A | 1 | Immediate ECC correction enabled |
| 5 | — | 0 | Reserved |
| 4 | — | 0 | Reserved |
| 3-0 | STEP | 0111 | Fast step option 7: 15 µs per step |
SUB49 (E48Eh) sends these 6 bytes to port 41h during the Command Phase
(status 0Bh). SUB43 (E3D0h) then reads 256 bytes from port 42h during
the Data In Phase (status 13h) using INIR with B=0.
SUB45 (E404h) builds the same 6-byte SASI command block, but with opcode 0Ah (Write) and control byte 07h (retries enabled, no ECC, step option 7).
SUB46 (E42Bh) waits for the Data Out Phase (status 03h) and writes 256
bytes to port 41h using OUTIR with B=0.
During boot, SUB47 (E449h) sends the WD1002-SHD Set Parameters command
(SASI opcode 0Ch, aka “Initialize Drive Characteristics”) followed by an
8-byte parameter block sent via SUB48 (E463h) during the Data Out Phase:
| Byte | Value | WD1002-SHD Parameter | Decoded |
|---|---|---|---|
| 0 | 01h | MSByte of Number of Cylinders | 0x0132 = 306 |
| 1 | 32h | LSByte of Number of Cylinders | |
| 2 | 04h | Number of Heads | 4 heads |
| 3 | 00h | MSByte of Reduced Write Current Cylinder | 0x0080 = 128 |
| 4 | 80h | LSByte of Reduced Write Current Cylinder | |
| 5 | 00h | MSByte of Write Precomp Cylinder | 0x0040 = 64 |
| 6 | 40h | LSByte of Write Precomp Cylinder | |
| 7 | 0Bh | Max ECC Error Burst Length | 11 bits |
These values precisely match the Seagate ST-212’s physical geometry (306 cylinders, 4 heads). The WD1002-SHD power-up defaults are for the ST-506 (153 cylinders, 4 heads, RWC=128, WP=64, ECC=11) — the BIOS only needs to change the cylinder count from 153 to 306; all other parameters happen to be the same between the ST-506 and ST-212.
The BIOS detects SV-608M presence using its Centronics printer ports 60h–63h (not the SASI ports):
SUB55 (E506h): Initialises the printer interface — 00h to
strobe (61h), aux (63h) and status (62h), FFh to data (60h),
then 34h to aux (63h) and strobe (61h).
SUB56 (E5A7h): Toggles bit 2 of AUX port (63h) and reads STATUS port (62h) before and after. If STATUS changes, the SV-608M is present (carry clear). If same, no hardware (carry set).
This detection runs in SUB59 (E8A6h) during cold boot. The result
determines both the LIST device routing (SV-608M printer vs standard
printer) and whether DATA69 (E89Fh) is set to 0 (present) or 1
(absent).
Standard SASI bus protocol. The entire SV-608M I/O protocol is textbook SASI (Shugart Associates System Interface). Port 46h is a direct readout of the 5 SASI control signals (BSY, REQ, C/D, I/O, MSG), and the 6-byte command blocks are standard SASI Group 0 format. Ports 41h/42h are the bidirectional SASI data bus with hardware-generated ACK handshake.
WD1002-SHD command set. The BIOS uses three WD1002-SHD commands: Set Parameters (0Ch) at boot, Read (08h), and Write (0Ah). The control byte configures ECC correction, retry policy, and step rate per-command.
All disk geometry is statically defined in the DPBs at compile time. The BIOS does not query the hard disk for its geometry — the ST-212’s 306 cylinders and 4 heads are hardcoded in the Set Parameters block.
Sector size is always 256 bytes for both floppy and HDD. The CP/M
128-byte logical sectors are paired internally by SETSEC_impl (DD13h),
which halves the sector number and tracks odd/even in DATA24 (E149h).
The WD1002-SHD handles all CHS translation. The BIOS sends a 16-bit linear sector address. The WD1002-SHD maps this to cylinder/head/sector internally using the parameters set by the 0Ch command. The Z80 never deals with physical CHS geometry.
64 tracks (16 cylinders) reserved. The ST-212 has 1,224 tracks but the BIOS only partitions 1,160. The unused 64 tracks at the inner edge are likely reserved for the WD1002-SHD’s bad track alternates.
No sector translation table is used (all DPH XLT pointers are 0000h).
SECTRAN_impl (DCE8h) simply adds 1 to the sector number (making hardware
sectors 1-based).
Extracted from images/stitched_cpm_227_608m_system_40ss.dsk. All
disassemblies were generated with
z80dismblr and post-processed to
replace raw port numbers with named EQU constants (see
svi328-port-map.md for the full port reference).
| File | Origin | Lines | Description |
|---|---|---|---|
| boot_sector.asm | C100h | 151 | Boot sector with FDC/PSG port EQUs |
| cpm_system.asm | D600h | 5,281 | BIOS with full port EQUs (RS-232, FDC, SV608M, 80-col, HDD, PSG) |
| ccp_bdos.asm | C000h | 5,539 | CCP + BDOS (no direct port I/O) |
Disassembly and analysis of the SVI-609 Bootstrap Card ROM (images/svi-609.bin, 4096 bytes).
| File | Description |
|---|---|
| svi-609.asm | Annotated disassembly (bootstrap stub at 0000h + main code at 8000h) |
| svi-609-analysis.md | Full analysis: boot mechanism, SASI/FDC protocol, comparison with CP/M BIOS |
| File | Description | Based on |
|---|---|---|
| svi328-port-map.md | SVI-328 I/O port reference (FDC, SASI, PSG, 80-col, etc.) | BIOS disassembly + WD1002-SHD manual |
| hdd-dump-program.md | HDD dump methodology and SASI protocol reference | BIOS SASI routines + WD1002-SHD manual + ST-212 specs |
| hdd-dump.asm | HDD dump utility — standalone ROM (org 0000h, VDP text output) | BIOS SASI routines + WD1002-SHD manual + ST-212 specs |
| svi-609-analysis.md | SVI-609 Bootstrap Card ROM analysis | svi-609.asm disassembly |
| msysgen.md | MSYSGEN.COM analysis — CP/M system generation (floppy ↔ hard disk) | MSYSGEN.asm disassembly |
| mformat.md | MFORMAT.COM analysis — hard disk and floppy disk formatter | MFORMAT.asm disassembly |
| File | Description |
|---|---|
| 79-000004_WD1002-SHD_OEM_Manual_Aug1984.pdf | Western Digital WD1002-SHD Winchester Disk Controller OEM Manual |
| Seagate-ST212-Hard-Disk.pdf | Seagate ST-212 Hard Disk Product Manual (Rev. B) |
| File | Size | Description |
|---|---|---|
| stitched_cpm_227_608m_system_40ss.dsk | 172,032 bytes | Clean CP/M 2.27 floppy disk image |
| svi-609.bin | 4,096 bytes | SVI-609 Bootstrap Card ROM dump |
| boot_sector.bin | 128 bytes | Track 0, sector 1 |
| cpm_system.bin | 5,248 bytes | BIOS (track 0 sec 2–18 + track 1 sec 1–12) |
| ccp_bdos.bin | 5,632 bytes | CCP + BDOS |
| track0_sec2_18.bin | 2,176 bytes | Intermediate: track 0 sectors 2–18 |
| track1_sec1_12.bin | 3,072 bytes | Intermediate: track 1 sectors 1–12 |
| File | Description |
|---|---|
| add_port_equs.py | Script to add port EQUs to disassemblies |
The BIOS accesses the following I/O port groups (functional roles inferred from code behaviour):