svi-328.dev

MFORMAT — 608M Disk Formatting Program

Disassembly analysis of MFORMAT.COM from the CP/M 2.27 system disk for the SVI-328 with SV-608M expansion. This utility formats both hard disks (via the SASI bus) and floppy disks (via the FDC), including bad track management and directory initialisation for the hard disk.

Source: MFORMAT.asm

Overview

MFORMAT is the low-level disk formatter for the SV-608M system. It provides two major functions:

At 7,232 lines of disassembly (roughly 5 KB of Z80 code plus 1 KB of string data, plus embedded BIOS routines at 4259h+), MFORMAT is by far the largest utility on the system disk.

User Interface

On startup, MFORMAT prints:

608M Disks Formatting Program

Press "H" to format a Hard disk, "F" to format a floppy disk
  or  ENTER  to quit):

After each format operation completes, control returns to the main menu.

Hard Disk Partition Menu

When H is selected:

Hard Disk Formatter
Press "A" for track   0 -  257
      "B" for track 258 -  513
      "C" for track 514 -  769
      "D" for track 770 - 1223
      "X" for track   0 - 1223
  or ENTER to quit):

Each partition offers a confirmation prompt:

Press ENTER to confirm or "Q" for quick option or Control-C to cancel):

Floppy Disk Menu

When F is selected:

Floppy Disk Formatter
Press "1" for Single Side or
      "2" for Double Side or
      ENTER to quit):

After formatting, asks:

Format another disk? (Y or N):

Hard Disk Format Mode

Track-to-Partition Mapping

The hard disk uses linear track addressing with 32 sectors per track and 256 bytes per sector (8 KB per track). The four partitions map to these track ranges:

Partition Key Track Range Tracks Size
A (= B:) A 0 – 257 258 ~2 MB
B (= C:) B 258 – 513 256 ~2 MB
C (= D:) C 514 – 769 256 ~2 MB
D (= E:) D 770 – 1223 454 ~3.5 MB
All X 0 – 1223 1224 ~9.5 MB

Drive letters in parentheses are for the floppy BIOS (A:=floppy, B:–E:=HDD). When booted from HDD (T0T1.SYS), partitions A–D map to drives A:–D: instead, with E: as the floppy.

Note: partition D is larger than the others because it covers the remainder of the disk.

Per-Partition Format (A/B/C/D)

Each partition is formatted by its own subroutine (SUB056–SUB059), but they all follow the same pattern:

  1. Prompt for confirmation (ENTER/Q/Ctrl-C)
  2. Loop over the partition’s track range:
    • Format track — SUB070 writes an E5h fill pattern to all 32 sectors via SASI Write (SUB005)
    • Verify track — SUB071 reads the track back via SASI Read (SUB009) and checks that all bytes are E5h (SUB072, checking 8 × 1024 bytes)
    • On error, display the hex error code (SUB073)
  3. Print track number as progress indicator during format and verify

The “quick” option (Q) skips the verify step by clearing DATA21 (the verify-enable flag checked in SUB071).

Full Disk Format (X)

Selecting X (SUB060) formats all 1,224 tracks (0–04C7h) in sequence, with two additional operations after formatting:

  1. Record bad tracks — any track that fails format or verify has its error code recorded in the bad track table via SUB081
  2. Bad track management — after all tracks are formatted, SUB061 is called to handle defective tracks

Bad Track Management

The bad track management subsystem (SUB061) is only activated after a full disk format (X). It maintains a bad track table at 1D22h — an array of 1,224 bytes (one per track), where:

Initialisation (SUB080): zeros all 1,224 bytes, sets the available track counter (SELF_MOD13) to 04C8h (1224).

Recording (SUB081): writes the error code at offset track_number in the table, decrements the available track counter.

After formatting, SUB061 displays the defect count and offers:

Total number of defective tracks are : NNN

Press "D" for displaying the defective track list
      "M" for calculating the auto-mapping sequence
      ENTER for quit):

After mapping completes, the disk size is updated on disk via SUB069.

Directory Marker — DSIZE:

SUB069 writes a DSIZE: directory marker to track 15, sector 0 (linear sector 21EAh in the buffer at that address). This marker is a 12-byte record:

Offset Content
0–5 ASCII DSIZE:
6–7 Current disk size (track count, LE)
8–11 #### — placeholder signature

The marker is written using a SASI Write command (SUB008) and verified on readback (SUB011) by checking for the DSIZE: + #### signature. This allows the system to determine the usable disk capacity at boot time, accounting for any tracks lost to bad track remapping.

SASI Write Command (Format Track)

SUB005 performs the SASI Write for formatting. It calculates the linear sector address as track × 32 using an ADD IX,DE loop (32 iterations), then issues the SASI transaction:

  1. Bus Free — wait for bus phase 00h (SUB053)
  2. Target Selection — write 01h to port 41h/43h
  3. Command — send 6-byte SASI Group 0 Write (opcode 0Ah) via SUB047:

    Byte Value Field
    0 08h Write with verify opcode
    1 LUN/SA LUN 0 + sector address bits 20–16
    2 SA Sector address bits 15–8
    3 SA Sector address bits 7–0
    4 04h Block count = 4 sectors (1,024 bytes)
    5 47h Control: retries + ECC, step = 15 µs
  4. Data Out — write 4 × 256 bytes of E5h fill to port 41h (SUB036)
  5. Status — read result from port 42h (SUB051)
  6. Message In — read completion byte from port 42h (SUB052)

The command is issued 8 times per track (8 × 4 = 32 sectors per track).

SASI Read Command (Verify Track)

SUB009 performs the SASI Read for verification. Same sector address calculation, with a 6-byte SASI Group 0 Read:

Byte Value Field
0 05h Read with verify opcode
1 LUN/SA LUN 0 + sector address bits 20–16
2 SA Sector address bits 15–8
3 SA Sector address bits 7–0
4 07h Block count = 7 sectors
5 07h Control: retries, step = 15 µs

Data is read into a buffer at 21EAh and compared byte-by-byte against E5h (SUB072). If any mismatch is found, the track is marked defective.

Floppy Disk Format Mode

Drive Detection

SUB084 detects the floppy drive type using the same technique as MSYSGEN:

  1. Reads the warm boot vector from address 0001h to derive the BIOS base
  2. Patches SUB091 and SUB092 with BIOS internal Read/Write entry points (WBOOT + 51h and WBOOT + 57h)
  3. Calls BIOS SELDSK(0) via a self-modifying JP instruction (SUB104)
  4. Reads the SPT field from the returned DPB
  5. If SPT = 34 (standard format), default drive = A:. Otherwise, E:
  6. Stores the drive number for BIOS call patching (SELF_MOD02)

Track Format Structure

Floppy formatting uses two distinct track layouts:

Track 0 — Single Density (128 bytes/sector):

Tracks 1–39 — Double Density (256 bytes/sector):

Tracks 40–79, Side 2 (double-sided only):

Sector Interleave (Skew Table)

The skew table at 0B54h defines the physical sector ordering to optimise sequential access:

01 0A 02 0B 03 0C 04 0D 05 0E 06 0F 07 10 08 11 09 12

This is a 2:1 interleave pattern — logical sectors are spread across the track so the disk has time to process one sector before the next logical sector arrives under the head.

The source code contains a note: “Skew table follows and may be patched for better response in your application” — indicating users could customise the interleave.

Format Sequence

For each track, SUB085 performs:

  1. Build format buffer at 21EAh (SUB101 for track 0 SD, SUB102 for DD)
  2. Seek — issue FDC Seek command (SUB093/SUB094: command 02h or 52h) and wait for completion (SUB095: poll INTRQ via port 34h)
  3. Check status — verify FDC is ready (SUB096: poll command/status port 30h for DRQ, with timeout)
  4. Write track — issue FDC Write Track command (SUB098: command F0h), DMA the format buffer to FDC data port using OUTI loop with INTRQ/DRQ polling
  5. Verify — read back and display “Verifying track NN”
  6. Report errors: write-protect (07h→”Error: Disk is write protected!”), format failure (general→”Error: Cannot format this disk!”), no disk (40h→”Error: There is no disk in that drive!”)

FDC Control

The FDC is accessed through three ports:

Port Direction Usage in MFORMAT
30h W/R FDC Command / Status register
34h W Disk Select (0Dh = drive select pattern)
34h R INTRQ/DRQ status (bit 7 = INTRQ, bit 6 = DRQ)
38h W Density: 00h=DD, 01h=SD, 02h=DD side 2

FDC commands used:

Memory Map

0100h         LBL1        — Entry: JP LBL2
0103h–011Ah   SUB003      — SASI bus reset for formatting
011Bh–0139h   SUB004/124  — SASI bus init, test disk ready
013Ah–01F1h   (data)      — Unreachable code/data region
01F2h–022Eh   SUB005      — SASI Write (format track, 32 sectors)
022Fh–026Bh   SUB006      — SASI Read (used by alternate mapping)
026Ch–02ABh   SUB007      — SASI Write (4 sectors, for directory)
02ACh–02EBh   SUB008      — SASI Write (4 sectors, for DSIZE)
02ECh–0328h   SUB009      — SASI Read (32 sectors, verify track)
0329h–0394h   SUB010      — SASI Data Copy (read + write, remap)
0395h–041Ah   SUB011      — SASI Read + validate DSIZE: marker
044Bh–048Ch   SUB012/013  — SASI address validation
048Dh–04B2h   SUB016/017  — SASI status check / result decode
04B3h–0582h   SUB018–024  — SASI command block builders
0583h–065Ch   SUB025–031  — SASI command parameter setup
065Dh–0892h   SUB030–041  — SASI read/write/format with verify
0893h–0A11h   SUB042–054  — SASI bus phases (select, cmd, data, status, msg)
0A12h–0A4Ah   DATA03–14   — SASI command buffer and state variables
0AFCh         DATA16      — Stack save area
0AFEh         LBL2        — JP LBL3 (startup redirect)
0B01h–0B53h   (strings)   — Skew table description text
0B54h–0B65h   (data)      — Sector skew table (18 bytes)
0B66h–0BB3h   LBL3        — Main menu: dispatch H/F/ENTER
0BB4h–0C20h   SUB055      — HDD format dispatcher (partition select)
0C21h–0C85h   SUB056      — Format partition A (tracks 0–257)
0C86h–0CEAh   SUB057      — Format partition B (tracks 258–513)
0CEBh–0D4Fh   SUB058      — Format partition C (tracks 514–769)
0D50h–0DB4h   SUB059      — Format partition D (tracks 770–1223)
0DB5h–0E15h   SUB060      — Format ALL partitions + bad track mgmt
0E16h–0EDFh   SUB061      — Bad track management menu (D/M/ENTER)
0EE0h–0F20h   SUB062      — Display defective track list
0F21h–0F58h   SUB063      — Scan bad track table for next defect
0F59h–0FE1h   SUB064      — Auto-map defective tracks to alternates
0FE2h–1027h   SUB065      — Find next available alternate track
1028h–106Eh   SUB066      — Write/verify alternate track
106Fh–10D5h   SUB067      — Copy + verify bad→alternate track data
10D6h–1113h   SUB068      — Find next bad track entry
1114h–1195h   SUB069      — Write DSIZE: directory marker to disk
1196h–11ACh   SUB070      — Format single HDD track (print + SUB005)
11ADh–11D3h   SUB071      — Verify single HDD track (print + SUB009)
11D4h–1224h   SUB072      — Verify E5h pattern (8 × 1024 bytes)
1225h–1256h   SUB073      — Display hex error code (* ERROR CODE : XXH *)
125Ah–128Bh   SUB075      — Print leading spaces for number alignment
128Ch–129Fh   SUB076      — Print decimal track number
12A0h–12A7h   SUB077      — Print space character
12A8h–12B5h   SUB078      — Print $-terminated string (BDOS 9)
12B6h–12BEh   SUB079      — Print CR/LF
12BFh–12E3h   SUB080      — Initialise bad track table (1224 zeros)
12E4h–12F9h   SUB081      — Record bad track in table
12FAh–1308h   SUB082      — Console output (BDOS 2)
1309h–1312h   SUB083      — Keyboard input (BDOS 6 polling)
1313h–1381h   SUB084      — Floppy format main: detect drive, patch BIOS
1382h–1514h   SUB085      — Floppy format loop (track 0 SD + tracks 1+ DD)
1515h–1520h   SUB086      — BDOS 6 direct console input (polling)
1521h–1529h   SUB087      — BDOS 2 echo character
152Ah–1538h   SUB088      — Print track number during floppy format
1539h–155Ah   SUB089      — Recursive decimal number print
155Bh–155Fh   SUB090      — Short delay (4× EX (SP),HL)
1560h–1569h   SUB091      — Patched BIOS Read (self-modifying JP)
156Ah–156Ch   SUB092      — Patched BIOS Write (self-modifying JP)
156Dh–1571h   SUB093      — FDC Seek command (02h)
1572h–1573h   SUB094      — FDC Seek with verify (52h)
1574h–1587h   SUB095      — FDC command issue + wait for INTRQ
1588h–15B0h   SUB096      — FDC status check with timeout
15B1h–15BBh   SUB097      — Print FDC error message (BDOS 9)
15BCh–15F2h   SUB098      — FDC Write Track command (F0h)
15F3h–15F9h   SUB099      — Wait for FDC not busy
15FAh–1605h   SUB100      — FDC Force Interrupt (D0h)
1606h–166Bh   SUB101      — Build SD format buffer (track 0, 18 sectors)
166Ch–16DFh   SUB102      — Build DD format buffer (tracks 1+, 17 sectors)
16E0h–16E6h   SUB103      — Fill buffer helper (B bytes of A)
16E7h–1D06h   (strings)   — All user-facing messages
1D07h–1D09h   (data)      — CR/LF + '$' (used by SUB079)
1D0Ah         DATA20      — Temp storage for error code display
1D0Bh         DATA21      — Verify enable flag (01h=verify, 00h=skip)
1D0Ch         SUB104      — Patched JP to BIOS SELDSK (self-modifying)
1D0Fh         SELF_MOD05  — FDC error flag
1D10h         SELF_MOD06  — Alternate mapping mode (0=display, 1=write)
1D11h         SELF_MOD07  — Sides: 01h=single, 02h=double
1D12h         SELF_MOD08  — Current FDC track number
1D13h         SELF_MOD09  — Current FDC side (00h or 01h)
1D14h–1D15h   SELF_MOD10  — Current bad/alternate track number
1D16h–1D17h   SELF_MOD11  — Alternate track number (for remap)
1D18h–1D19h   SELF_MOD12  — Bad track count
1D1Ah–1D1Bh   SELF_MOD13  — Available track count (starts at 04C8h)
1D1Ch–1D1Dh   SELF_MOD15  — Bad track scan pointer (forward)
1D1Eh–1D1Fh   SELF_MOD16  — Alternate scan pointer (backward)
1D20h–1D21h   SELF_MOD17  — Remaining tracks to scan
1D22h–21E9h   SELF_MOD18  — Bad track table (1224 bytes, one per track)
21EAh+        (buffer)    — I/O buffer for format/verify data
4259h+        (code)      — Relocated BIOS routines (SELDSK dispatch etc.)

CP/M BDOS Functions Used

Function C reg Name Usage in MFORMAT
2 02h Console Output Echo typed characters, print digits (SUB082)
6 06h Direct Console Non-blocking keyboard input (SUB086)
9 09h Print String Display $-terminated messages (SUB078)

Self-Modifying Code

MFORMAT uses extensive self-modifying code, more so than MSYSGEN:

Error Handling

Hard Disk Errors

Floppy Disk Errors

All error messages are preceded by a BEL character (07h) for audible alert.

Timeouts

Like the BIOS and MSYSGEN, MFORMAT has no timeout on SASI bus phase polls. If the hard disk controller hangs, the program will busy-wait forever. The FDC status check (SUB096) does have a finite timeout loop (counts down HL from 0000h twice).

Relationship to Other Utilities