A measurement, evaluation, and plotting system for Differential Thermal Analysis (Differentialthermoanalyse) on the TRS-80 Model I, written in Z80 assembler.
Author: Gerd Schmidtke, Dipl.-Ing. thesis (Diplomarbeit), FH Münster, 1984 Copyright strings (binary-verified):
'<C> 1984 by Gerd Schmidtke, FH Muenster'(DTA.ASS line 590)'* * * Differential - Thermo - Analyse * * *'(DTA.ASS line 610)
This source code and program are close to my heart, even though I didn't wrote them. The memories run deep, because back in those days the TRS-80 M1 represented a first real capability to measure lab data, store it, and then reuse it — to check, control, and optimize.
And there is more. Working as a chemist in industry, I built numerous process-automation systems in my labs, work that later led to one of the first client-server-based LIMS systems, to name just one example. Why am I telling this here? At CWH (Chemische Werke Hüls — the line runs IG Farben → CWH → Hüls AG → Degussa-Hüls → today's Evonik Industries), two TRS-80 M1 machines measured waste-water quality 24/7 in one of the labs, the CWH Umweltlabor — until the early 1990s. Around the clock, for more than a decade. Let that sink in.
These machines ran NEWDOS/80 — a highly capable DOS whose open, user-modifiable design suited unattended measurement duty in a way stock TRSDOS did not. Seen with today's knowledge and technology, this is all the more astonishing: a machine Tandy initially expected to sell in the low thousands went on to sell roughly a quarter million units — and ended up doing serious laboratory work. Bespoke lab software of this kind was almost never preserved; this Diplomarbeit code is among the few surviving sources documenting what was actually done with this unique computer in professional measurement settings.
The TRS-80 was coupled to a DTA instrument for the thesis; the printed Diplomarbeit is currently lost. The surviving sources below were rescued from disk. Note: no relation to the HRG-1B manufacturer — the card is by RB (Rolf Best) Electronic GmbH, Eitorf.
Source disk: esnd-40.dmk — see the disk catalog
entry for the full file listing.
A sample and an inert reference are heated on the same temperature program. The instrument records the temperature difference ΔT between them. When the sample undergoes a thermal transition — melting, crystallization, decomposition — it absorbs or releases heat and ΔT deviates from zero, producing a peak in the ΔT-vs-time curve. The area of that peak, measured against a baseline, is proportional to the enthalpy of the transition. This is why the evaluation module below is built entirely around baseline construction and peak-area integration.
DTA/CMD is the master menu. On start it loads the resident HRG graphics driver
(HRG/DUM), checks/initializes FORMFILE (the sample-header form), then offers:
| Menu item | Program | Status |
|---|---|---|
| 1 — Messwerterfassung | AUFNAHME/CMD |
missing — not on esnd-40, not in catalog |
| 2 — Messwertauswertung | AUSWERT/CMD |
binary missing; full source survives (AUSWERT/ASS) |
| 3 — Messkurven Plotten | PLOT/CMD |
missing |
| 4 — Formfile neu initialisieren | (built into DTA/CMD) | present |
| 5 — Programmsystem verlassen | — | — |
Data files: FORMFILE (1024-byte header form), INHALT (data index, missing),
measurement files named by the user (Dateiname : ......../...).
Measurement values are stored as single bytes (verified: LD A,(IX+0) reads
per sample in GRAPH2/SUMV1) — i.e. 8-bit ΔT samples, implying an 8-bit ADC in
the missing acquisition module (inference; the converter itself is not in the
surviving code).
The peak-area computation (FLAECH → BASIS → FNTFL1) works as follows:
- Display & scaling. The curve is drawn on the HRG (384×192) via the driver
line routine at FC04H. Autoscaling:
FAKT1 = YFAK/(max−min),FAKT2 = XFAK/(x_max−x_min); screen Y = 128 − (y−min)·FAKT1 (DEHN,MINMAX,GRAPH2). - Boundary selection. The user moves an inverted 14-pixel cursor with the
arrow keys to mark first the left, then the right peak boundary (
CURSER), with interval validation ("Intervall zu klein"). - Baseline. A straight line is drawn between the curve values at the two
boundaries;
FLBAS= the trapezoid area beneath it: Δx·(y₁ + (y₂−y₁)/2). X values are pre-scaled by 0.01 (FAKT4) to keep higher powers within floating-point range. - Piecewise least-squares cubic fit. The interval is split into chunks of
8–150 samples (
BASUP0/2/3adjusts the chunk count until this holds). Per chunk, the normal-equation sums Σxᵏ and Σy·xᵏ are accumulated in double precision (SUMV1/SUM/XSUM/YSUM) and the 4×4 system is solved by matrix inversion (INVERT, withUP2as 4×4 matrix multiply) — four coefficients, i.e. a cubic. (The source comment says "Ausgleichsparabeln"; the matrix dimensions show it is actually a cubic fit.) - Analytic integration.
FNTFLforms the antiderivative f₄x⁴/4 + f₃x³/3 + f₂x²/2 + f₁x — the stored constantsFAKT5..7= 1/4, 1/3, 1/2 are these divisors — evaluates it at both chunk ends (Horner scheme,KUBFNT), and accumulates the differences intoFLPEEK(double precision). - Result. Peakfläche =
FLPEEK−FLBAS, converted to ASCII and shown; optional printer output with "Drucker nicht bereit" handling, and refusal while the plot program is resident.
Fitting a smooth polynomial before integrating suppresses quantization/ADC noise instead of integrating it — a sound numerical design given 8-bit samples, and notably careful work for 1984 Z80 assembler. Floating point uses the Level II ROM routines (single 4-byte / double 8-byte, type flag at 40AFH, WRA1 at 4121H); only the matrix algebra is hand-rolled.
| File | Type | Content (binary-verified) |
|---|---|---|
DTA/ASS |
EDTASM source | Master menu, ORG 5200H. FCBs for HRG/DUM, FORMFILE, INHALT, AUFNAHME/CMD, AUSWERT/CMD, PLOT/CMD. Full-screen form editor for FORMFILE (arrow keys, SHIFT-arrows = delete/insert char, CLEAR = save). Buffers: FE00H (Inhalt), FF00H (Messwerte), 7000H (Messwertspeicher). |
AUSWERT/ASS |
EDTASM source, 1779 lines, fully commented | Evaluation module, ORG 5832H. See algorithm above. |
HRG/DUM |
/CMD load file, 780 bytes | Memory dump of a resident HRG BASIC-extension driver, load range FB10H–FDFFH, entry 0000H (dump, no autostart). Embedded copyright: (C) 1983 Dieter Bolz. Drives ports 00H–05H — the RB Electronic HRG-1B port map. Intercepts BASIC tokens (82H, 83H, 84H, 9CH, A2H, A6H, A9H, B8H, C6H) via the 4004H vector. Not derived from HRG/CMD: byte comparison shows 746 of 752 overlapping bytes differ. |
HRGDUM/ASS |
Disassembly | Disassembly of HRG/DUM, label-free (addresses literal). Byte-verified against HRG/DUM on the opening instruction sequence at FB10H (exact match); full-length diff pending. |
DTAST/ASS |
Disassembly fragment | Truncated copy of the same disassembly (first ~34 lines). |
CODE/ASS |
Disassembly fragment | Middle section of the same disassembly; contains the comment ;LAUT LISTING: LD B,D — the disassembly was cross-checked against a printed listing, presumably from the Diplomarbeit. |
FORMFILE |
Data, 1024 bytes | Verbatim dump of the Model I video RAM (3C00H–3FFFH) used as an editable screen mask — the SAVE routine in DTA/ASS writes the screen byte-by-byte (LRECL=1) into this file and reloads it on start. Begins name + blanks. |
Also on esnd-40 but not yet pulled into this folder: TEST/ASS, WECKER/ASS,
TESTTEXT/CMD.
- DOS file I/O uses the NEWDOS/80 entry points 4420H (open/create), 4424H (open
existing), 4428H (close), 4436H (read), 4467H (print string) — consistent with
the NEWDOS/80 system on esnd-40. Disk I/O is bracketed by
STOP/START1, which patch the interrupt vector at 4012H (RET/JP) to disable the heartbeat during transfers. - HRG/DUM vs HRG/CMD — byte-compared, unrelated.
HRG/CMD(9,253 bytes, DB00H–FF24H, entry EB00H) also occupies FB10H–FDFFH, but with completely different code there: 746 of 752 overlapping bytes differ. Both drivers address ports 00H–05H (HRG-1B), but HRG/DUM is a third program — Dieter Bolz's compact 1983 BASIC-extension driver — not a variant of HRG/CMD.
RB Electronic HRG-1B (I/O ports 00H-05H)
▲ ▲
│ drives │ drives
HRG/CMD (9.2K driver, Dieter Bolz driver, (C) 1983
DB00-FF24, entry EB00) resident at FB10-FDFF
separate lineage, │
unrelated code │ memory dump (Schmidtke)
▼
HRG/DUM (752 bytes)
│
│ disassembled
▼
HRGDUM/ASS ◄── cross-checked against
│ printed listing
│ (";LAUT LISTING" in
│ CODE/ASS; DTAST/ASS
│ is a fragment copy)
│ yields internal entry points
▼
AUSWERT/ASS: LINE1 EQU 0FC04H,
coordinate cells FD7BH-FD81H
(direct calls, BASIC bypassed)
│
▼
DTA-Programmsystem (DTA/CMD menu → AUFNAHME* / AUSWERT / PLOT*)
│
FORMFILE = video-RAM screen mask (3C00H dump)
│ same technique, 1984
▼
maske1/dum, maske2/dum — expert system, 1991
(* = binary lost)
- Working method (binary-verified): the resident Bolz driver was dumped
from memory (
HRG/DUM), disassembled (HRGDUM/ASS, cross-checked against a printed listing per the;LAUT LISTINGcomments inCODE/ASS), and its internal routines were then reused directly from assembler —AUSWERTdeclaresLINE1 EQU 0FC04Hand writes coordinates straight into the driver's cells at FD7BH–FD81H, bypassing the BASIC token interface entirely. Dump → disassemble → call internals: locating usable entry points in source-less binaries, the same pattern seen elsewhere in this archive. - Screen-mask technique:
FORMFILEis a pre-rendered full-screen form saved as a raw video-RAM dump and reloaded in one pass — conceptually identical tomaske1/dum/maske2/dumin the expert system (1991), which draws forms once withmaskgen.basand reloads them viaCMD"load". The DTA system (1984) is the earlier of the two occurrences in this archive; here the mask is additionally editable in place via the built-in full-screen editor. The same idiom appears in its lighter form as the dotted fill-in fieldDateiname : ......../...(TEXT1, written to video RAM at 3F80H, the filename over-typed in place at 3F8CH) — the fill-in-form UI of the expert system'swbedit, five years earlier.
The evaluation pipeline was brought back to execution in sdltrs, 42 years
after the Diplomarbeit. Work disk: dta-work3.dsk (a copy-chain from
esnd-40.dmk; the original image is untouched). PDRIVE for the disk:
TC=40, SPT=10, TSR=0, GPL=2, DDSL=17, DDGA=2.
Original components, restored: DTA/CMD (on-disk original), FORMFILE
(restored from its KILLed entry; byte-identical to the independently
preserved copy), HRG/DUM (on-disk original, loaded resident by DTA).
Modern additions, clearly non-original: AUSWERT/CMD rebuilt from
AUSWERT/ASS with zmac (original binary lost); INHALT and PEAK1/DAT /
PEAK2/DAT synthesized (see below). These live only on the work disk.
INHALT format, decoded from the AUSWERT parser (binary-verified): a
plain byte stream of measurement-file names, each terminated by 0DH; byte
03H is skipped as filler; a :d drive suffix is displayed but terminated at
the colon for the FCB. On read past EOF (DOS error 28) the file is rewound
via 443FH — the down-arrow cycles through the list. INHALT was presumably
written by the lost AUFNAHME/CMD.
Measurement-file format: AUSWERT computes the sample count as file-size/2 and reads that many sequential bytes — the first half of the file holds the 8-bit ΔT samples; the second half's purpose is unknown (AUFNAHME would tell). Synthetic files were built accordingly: 200 samples (Gaussian peak, height 150 resp. 75, sigma 15, on a drifting baseline of ~40) plus 200 padding bytes.
Validated at runtime: menu → FORMFILE found (no init detour) →
AUSWERT/CMD chained and executed → INHALT parsed, Dateiname : PEAK1/DAT:0
displayed → ENTER → curve loaded, autoscaled, drawn on the HRG via the
resident Bolz driver (LINE at FC04H) → Flaechenberechnung: boundary cursors,
baseline, piecewise cubic fit, Peakfläche displayed with printer option.
The file-size/2 sample-count reading is confirmed (complete curve, no
truncation).
Peakfläche observations: the result is strongly sensitive to boundary
placement — three runs on identical data gave 3.19314, 0.898252 and
−1.66937. The mechanism: the baseline is the chord between the curve values
at the two chosen boundaries; an endpoint on a peak flank inflates FLBAS and
can push the signed result negative (the code reports the sign honestly).
This operator-dependence is authentic to 1980s manual-baseline DTA practice.
OPEN: the absolute scale of the number (which coordinate space FLAECH
integrates in) is not yet established; a linearity test (PEAK2 = half
height, expected ratio 0.5 for raw-value integration vs ~1.0 for autoscaled
coordinates) is prepared on dta-work3.dsk and pending.
Screen-clear observation: the source clears the HRG before every plot
(GRAPH opens with CLS + HRGCLS; HRGCLS calls the Bolz driver's clear
routine at 0FBCFH — verified in the dump as a 48×256 = 12,288-write loop,
exactly the card's video RAM size). In the 2026 runtime environment the
routine executes but the screen does not wipe, so successive plots overlay.
The source's intent is unambiguous; whether the clear worked on the 1984
hardware cannot be determined from the surviving material.
- Locate
AUFNAHME/CMD(or source) on remaining un-imaged disks — it contains the instrument/ADC interface, would document the hardware coupling, and would settle the measurement-file second-half format and the true INHALT writer. - Locate
PLOT/CMDand the originalAUSWERT/CMDbinary (a zmac rebuild from AUSWERT/ASS now exists and is runtime-validated, but a recovered original would allow a byte-diff against the rebuild). - Peakfläche coordinate space: run the prepared linearity test on
dta-work3.dsk(PEAK1 vs PEAK2, same boundaries; ratio ≈ 0.5 → raw-value integration, ≈ 1.0 → autoscaled coordinates). - Identify Dieter Bolz and the published form of his 1983 driver. Lead: an
HRG-1B cassette driver (
grl2.cas, loaded via SYSTEM) is documented in a restoration thread on forum.classic-computing.de — obtain and byte-compare against HRG/DUM. Possible channels: the Genie archive at oldcomputers.dyndns.org (hosts HRG-1B documentation), RB Electronic material. - Identify the origin/author of
HRG/CMD(the large 9.2K driver) — now known to be a separate lineage from HRG/DUM. - Complete the full-length instruction diff of
HRGDUM/ASSagainstHRG/DUM(opening sequence already verified). - Diplomarbeit recovery: 1984 FH Diplomarbeiten were rarely library-cataloged; the responsible Fachbereich (Steinfurt) is the more promising contact than the Hochschulbibliothek.