# SDH.COM User Manual

SDH.COM is a CP/M utility that talks to the SDH-100 firmware. It runs on the S-100 CPU like any other .COM program but instead of doing its work in CP/M, it hands the request to the SDH-100 and streams the result back to the console. Use it to mount and eject floppy images, transfer the live boot.conf, inspect the system memory map, view the boot log, and check or set the on-board real- time clock.

The utility is single-shot: each invocation parses one command line, executes one sub-command, prints the result, and returns to CP/M. There is no interactive prompt.

# Invocation

A>SDH <sub-command> [arguments]

SDH with no arguments or with an unrecognised keyword returns:

A>SDH
Unknown sub-command.
A>

CP/M folds the command tail to upper-case before passing it to SDH.COM, so you can type the keywords and arguments in any case and they reach the firmware as upper-case. All keywords are exactly two characters except for ?. Arguments are separated by spaces; tabs work the same as spaces.

SDH.COM lives on whichever CP/M drive you copied it to and is typically kept on the system drive A:. It depends only on the SDH-100 firmware -- it makes no use of any specific BIOS or CP/M extension and runs unchanged on any disk image that boots CP/M on the S-100 bus.

# Help

# ? -- this help

SDH ?

Lists every sub-command the firmware accepts in this build. The output reflects compile-time options: the SD/LD/MD/ED block appears only when the IMSAI FIF floppy controller is included, and SR/UR appear only when the on-board real-time clock is included.

A>SDH ?
Available sub-commands:
  ? : this help
  GC : Get boot.Conf
  PC : Put boot.Conf
  SD : Show Disk map
  LD : List Disk library
  MD : Mount Disk <A|B|C|D> diskimage.dsk
  ED : Eject Drive <A|B|C|D>
  SI : Show SDH-100 Info
  SE : Show Environment
  SL : Show boot Log
  SM : Show Memory map
  VS : Verify Shadow RAM
  RS : Reset/Resync Shadow RAM
  SR : Show RTC
  UR : Update RTC YYYY/MM/DD hh:mm:ss
A>

The ? listing is generated live from the firmware's command table, so it is always the canonical reference for the current build.

# Configuration transfer

# GC -- Get boot.Conf

SDH GC

Copies the live /CONF/boot.conf file from the SDH-100's microSD card into the current CP/M drive as BOOT.CFG. Newlines are converted to CP/M's CR-LF convention so the resulting file opens cleanly in WordStar, ED, or any other CP/M editor. If a BOOT.CFG already exists on the current drive it is overwritten.

A>SDH GC
Done.
A>DIR BOOT.CFG
A: BOOT     CFG
A>

GC does not modify boot.conf on the SD card -- it is a one-way copy from the SDH-100 to CP/M.

# PC -- Put boot.Conf

SDH PC

Reads BOOT.CFG from the current CP/M drive and writes it back to the SDH-100 as /CONF/boot.conf, replacing the existing file. CP/M CR-LF newlines are converted back to single LFs as the firmware prefers them.

New config takes effect on next boot

The new configuration takes effect on the next boot of the SDH-100; the running system continues to use the values it loaded at start-up.

A>SDH PC
Done.
A>

PC is the standard way to edit configuration without taking the SD card out: pull the file with GC, edit it under CP/M, then push it back with PC. See the boot.conf configuration guide for directive details.

# Disk management

These four sub-commands appear only when the firmware is built with the IMSAI FIF floppy controller. If your ? listing does not show them, the FIF emulator is not enabled.

# SD -- Show Disk map

SDH SD

Lists each emulated floppy drive and the image file currently mounted in it. Empty drives appear with no filename after the =. A leading > on the image name marks it as served by the attached FDC server rather than read from the local /DISKS/ directory. The output reflects the live runtime state.

A>SDH SD
DSK:A: = cpm22.dsk
DSK:B: = >wordstar.dsk
DSK:C: = >A.fdc
DSK:D: =
A>

# LD -- List Disk library

SDH LD

Lists every disk image available to the system. The local /DISKS/ directory on the SDH-100's microSD card holds .dsk image files; if an FDC server is attached over USB or UART, its catalogue of .dsk and .unpacked images is appended to the list. Each remote entry is shown with a leading >. If a physical floppy disk controller is wired through the FDC server, its two hardware drives appear as >A.fdc and >B.fdc.

A local-only example (no FDC attached):

A>SDH LD
cpm22.dsk
utilities.dsk
A>

With an FDC server attached and a physical floppy controller wired to it:

A>SDH LD
cpm22.dsk
utilities.dsk
>wordstar.dsk
>turbo.unpacked
>A.fdc
>B.fdc
A>

The leading > is part of the name as far as MD is concerned -- pass it through verbatim to mount a remote image.

# MD -- Mount Disk

SDH MD <A|B|C|D> diskimage.dsk

Mounts a disk image into the named drive. Both arguments are required; the drive letter must be A, B, C, or D, and the image name is the filename as it appears in the LD listing. A bare filename refers to a local image in /DISKS/; an FDC-served image must be passed with its leading > exactly as LD shows it.

A successful mount prints:

A>SDH MD A cpm22.dsk
Drive DSK:A: mounted successfully.
A>

The new mapping is written to disk.map on the SD card so it survives a reboot. Remote (FDC-served) mounts are tracked at the FDC server end and are not persisted in the local disk.map.

The error messages are specific:

A>SDH MD A
Usage: MD <A|B|C|D> diskimage.dsk
A>SDH MD E cpm22.dsk
Invalid disk letter. Use A, B, C, or D.
A>SDH MD A no-such-image.dsk
Disk image: no-such-image.dsk not valid.
A>SDH MD A cpm22.dsk
Drive DSK:A: is not empty.
A>SDH MD B cpm22.dsk
Disk image: cpm22.dsk already inserted in another drive.

Drive DSK:X: is not empty. means there is already an image in that drive -- eject it with ED first. Disk image: ... already inserted in another drive. means the same image file is currently mounted somewhere else; CP/M expects each image to appear in at most one drive at a time.

# ED -- Eject Drive

SDH ED <A|B|C|D>

Ejects whatever is in the named drive. The drive letter is required and follows the same rules as MD. The change is written to disk.map and persists across reboots.

A>SDH ED B
Drive DSK:B: ejected successfully.
A>

If the drive is already empty:

A>SDH ED B
Drive DSK:B: is already empty.
A>

# Information and diagnostics

# SI -- Show SDH-100 Info

SDH SI

Prints firmware version, board name, system clock, on-board temperature, and stack and heap usage. Use this to confirm what firmware is running and whether the system is healthy. Stack high- water marks are useful for spotting builds that are getting close to running out of stack on either core.

A>SDH SI
SDH-100 - Software Defined Hardware for the S-100 bus (RP2350B)
Binary: s100 ; Version: 0.18.0
Board: pico2
System Clock Frequency: 384.0 MHz
Board temperature:      32.4 C (raw: 1234)
Core0 stack size:       16384
Core0 stack HWM:        1024
Core1 stack size:       4096
Core1 stack HWM:        2048
Heap size:              262144
Heap free:              198432
A>

# SE -- Show Environment

SDH SE

Lists every key-value pair the firmware loaded from boot.conf at boot, two leading spaces per line. This is the authoritative view of what configuration the running system is actually using -- in particular, it shows you what the parser saw, which is more useful than re-reading the source boot.conf if you suspect a typo or case error.

A>SDH SE
  UART1=115200,cs8
  HARDDISK=blank
  ROM_LOAD=memon80.hex
  RAM_MAP=AUTO
  RAM_SET=0x00
  MEM_TEST=RUN
  BOOT_JUMP=0xF800
  FDC.transport=USB
  LPT.iobase=default
  LPT.device=S132
  ...
A>

The list contains every directive the parser accepted, including ones that were ignored by the consumer (for example, a typo that the parser stored but no subsystem ever read).

# SL -- Show boot Log

SDH SL

Streams the captured boot-time log out to the CP/M console. This is the same log that goes to UART0, the S-132 console, and the USB MON channel during start-up; SL lets you read it after boot without having to capture it on the host side.

A>SDH SL
... [full boot log content] ...
A>

# SM -- Show Memory map

SDH SM

Prints the page-by-page memory map: one character per 256-byte page, 32 pages per row, 8 rows total covering all 64 KB. The legend header explains the codes:

  • R -- emulated RAM provided by the SDH-100.
  • O -- emulated ROM provided by the SDH-100.
  • r -- shadow tracking real physical RAM on the S-100 bus.
  • X -- clash: physical RAM exists where the SDH-100 mapped a page (rendered in red on a colour-capable terminal).
  • . -- empty (no memory at this page).
A>SDH SM
Memory map: R=RAM, O=ROM, r=Shadow, X=Clash, .=empty
        0000: RRRR RRRR RRRR RRRR RRRR RRRR RRRR RRRR
        2000: RRRR RRRR RRRR RRRR RRRR RRRR RRRR RRRR
        4000: RRRR RRRR RRRR RRRR RRRR RRRR RRRR RRRR
        6000: RRRR RRRR RRRR RRRR RRRR RRRR RRRR RRRR
        8000: RRRR RRRR RRRR RRRR RRRR RRRR RRRR RRRR
        A000: RRRR RRRR RRRR RRRR RRRR RRRR RRRR RRRR
        C000: RRRR RRRR RRRR RRRR RRRR RRRR RRRR RRRR
        E000: RRRR RRRR RRRR RRRR OOOO OOOO OOOO OOOO
A>

The address printed at the start of each row is the first byte of that row, not the page number.

# Shadow RAM

The SDH-100 keeps a local "shadow" copy of every page of RAM it has provided to the CPU. The shadow is what services CPU reads from emulated RAM; the firmware also uses it for monitor commands that do not need to arbitrate the bus. These two sub-commands let you verify or refresh that shadow against the physical SRAM the CPU has been writing to.

# VS -- Verify Shadow RAM

SDH VS

Reads every shadow-mapped page from physical SRAM and compares it against the firmware shadow. Prints the address and the differing bytes for every mismatch, then Finished.. A clean run shows only the start and finish lines.

A>SDH VS
Scanning shadow RAM...
Finished.
A>

If the shadow has drifted -- for example, after a stack-corruption incident or a DMA glitch -- mismatches appear:

A>SDH VS
Scanning shadow RAM...
 0123: AB != CD
 0124: 00 != FF
Finished.
A>

The first byte is the firmware's shadow value, the second is the value just read from physical SRAM. VS is read-only -- it diagnoses but does not fix.

# RS -- Reset/Resync Shadow RAM

SDH RS

Walks every shadow-mapped page and overwrites the firmware shadow with the value just read from physical SRAM. After this, a VS run should be silent. Use this when you have used VS to confirm a drift and want the shadow to match what the CPU actually has in SRAM.

A>SDH RS
Resetting/resyncing shadow RAM...
Finished.
A>

RS does not write anything to physical SRAM -- it only updates the firmware's local copy. The CPU's view of memory is untouched.

# Real-time clock

These two sub-commands appear only when the firmware is built with the on-board DS3231 real-time clock.

# SR -- Show RTC

SDH SR

Prints both the hardware RTC reading and the firmware's POSIX system time on two lines. If the RTC chip is not detected on the I2C bus the first line is replaced with RTC NOT FOUND.

A>SDH SR
RTC: Sun, 2026/04/29 14:30:00
SYSTEM TIME: Sun Apr 29 14:30:00 2026
A>

The two lines should normally agree; if they drift, the system time has been adjusted at runtime (for example by an NTP client) without writing back to the hardware -- run UR to put the hardware in sync.

# UR -- Update RTC

SDH UR YYYY/MM/DD hh:mm:ss

Writes the supplied date and time to the DS3231 hardware register and updates the firmware's system clock to match. The two arguments are mandatory and must use slashes for the date and colons for the time. The firmware reads the new time back and prints it as a confirmation.

A>SDH UR 2026/04/29 14:30:00
New RTC time: Sun, 29/04/26 14:30:00
A>

If the arguments are missing or malformed:

A>SDH UR 2026/04/29
Usage: sdh ur YYYY/MM/DD hh:mm:ss
A>

If the RTC chip is not present, UR prints RTC NOT FOUND and makes no change.

# Common errors

Unknown sub-command.

You typed a keyword the firmware does not recognise. Run SDH ? to see what the current build supports.

Failed to open file

A sub-command needed to open a file on the SDH-100 SD card and could not. The most common cause is a missing /CONF/boot.conf during GC, or insufficient space on the card during PC. Check the SD card from the host over USB MSC to confirm what is there.

Out of memory

The firmware could not allocate the ~5 KB working buffer it needs to process a sub-command. The system is under memory pressure; reset the board.

No file opened.

An internal sequencing error -- usually means a previous command left state in an unexpected place. Reset the board and retry.

Last Updated: 5/2/2026, 5:36:29 PM