svi-328.dev

CP/M 2.27 for SV-608M — Disk Analysis

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).

Disk Format

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

System Layout

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)    

Memory Map

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 Sequence

  1. SVI-328 BIOS loads track 0, sector 1 to C100h and jumps there
  2. Boot sector configures PSG register 15 for memory banking (88h←0Fh, 8Ch←DDh)
  3. Reads 17 sectors from track 0 (128-byte mode) into D600h
  4. Switches FDC to 256-byte sector mode
  5. Reads 12 sectors from track 1 into DE80h
  6. Jumps to D600h (BIOS cold boot entry)
  7. BIOS BOOT_impl (DFB2h): disables interrupts, initialises memory mapper, timer, RST 38h vector, and function keys
  8. Sends reset to SV-608M SASI host adapter via OUT (40h), 00h (SUB39 at E227h — de-asserts all SASI bus signals)
  9. Detects 80-column card (SUB09 at DC46h — probes port 50h/51h)
  10. Initialises RS-232 UART (SUB10 at DCB1h — ports 28h–2Dh)
  11. Prints banner: Spectravideo CP/M-80 Revision 2.27 For SV-608M Booted From Floppy Disk
  12. Enters SUB32 (E09Bh): sets up zero-page vectors (0000h → JP D603h, 0005h → JP C806h)
  13. Calls 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).
  14. Reads CCP+BDOS (22 sectors from track 1, sector 13) into C000h
  15. Patches BDOS hooks for serial I/O interception
  16. Jumps to CCP at C000h

Hardware Identification

SASI Controller: Western Digital WD1002-SHD

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.

Hard Disk: Seagate ST-212

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).

SV-608M SASI Host Adapter

The SV-608M card provides a register-mapped interface to the raw SASI bus signals. The BIOS accesses two separate port groups:

SASI Bus Interface (ports 40h–46h)

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

Status Register (port 46h) — SASI Bus Signal Mapping

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.

SASI Transaction Sequence

A complete SASI transaction, as performed by the BIOS, follows this sequence:

  1. Bus Free (00h) — SUB53: Poll until (46h & 1Fh) == 00h
  2. Target Selection — Write target address 01h to port 41h (data bus), then write 01h to port 43h (assert SEL). The WD1002-SHD responds by asserting BSY.
  3. Command Phase (0Bh) — SUB50: Poll for 0Bh, then write each command byte to port 41h. The 6-byte SASI Group 0 command block is sent.
  4. Data In Phase (13h) — SUB43: Poll for 13h, then INIR 256 bytes from port 42h. (For writes: Data Out Phase, 03h, OUTIR to port 41h.)
  5. Status Phase (1Bh) — SUB51: Poll for 1Bh, read 1-byte status from port 42h. Bit 1 = error flag.
  6. Message In Phase (1Fh) — SUB52: Poll for 1Fh, read 1-byte completion (always 00h, signals bus freed).

SV-608M Centronics Printer / Detection (ports 60h–63h)

Ports 60h–63h are a Centronics parallel printer interface on the SV-608M expansion unit, also used for hardware presence detection:

At boot, SUB59 (E8A6h) calls SUB55 and SUB56, then routes the CP/M LIST device:

Boot Impact

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.

Hard Disk Access Analysis

Drive Configuration

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.

Drive Maps

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:

Floppy BIOS (booted from floppy, A:=floppy)

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

HDD BIOS (booted from HDD via T0T1.SYS, A:=HDD)

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):

Floppy DPB Variants

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.

80-Track Floppy DPB

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.

HDD Partition Layout

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.

Physical Access Protocol (via SASI)

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.

Linear Sector Addressing

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.

Read Command (SUB41 → SUB42, SUB43)

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.

Write Command (SUB44 → SUB45, SUB46)

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.

Controller Initialisation — Set Parameters (SUB47/SUB48)

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.

SV-608M Detection (SUB55, SUB56)

The BIOS detects SV-608M presence using its Centronics printer ports 60h–63h (not the SASI ports):

  1. 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).

  2. 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).

Key Observations

Files

CP/M System Disassemblies

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)

SVI-609 Bootstrap Card

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

Documentation

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

Reference PDFs

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)

Binary Images

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

Utilities

File Description
add_port_equs.py Script to add port EQUs to disassemblies

Port Groups Used

The BIOS accesses the following I/O port groups (functional roles inferred from code behaviour):