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