Files
opencode.energia.arti.ee 005dfb02d5 docs: how to use the 1990 r5.01 (OS/2) TMS34010 toolchain
The r5.01 tools are a superset of the 1987 DOS set: ANSI C, one driver,
gspopt, a bigger runtime, and 35.8% smaller .text at -o2 on the same
source. Add a practical guide:

* what the package contains and how to extract it
* running it: OS/2 1.3 under 86Box (full toolchain) and 2ine (assembler/
  linker/converter, with the patched fork linked)
* the gspcl option table and verified command lines (-o0/-o1/-o2, -pk,
  -al, -z/-lrts.lib), plus the manual gspac -> gspopt -> gspcg -> gspa
  pipeline and the linker command file
* gotchas: -o semantics, bit-addressed section sizes, CRLF, AUTOEXEC not
  running in the guest, emulated-keyboard limits, one-way assembler
  compatibility
* measured code-size and throughput numbers for both toolchains

Link it from the docs index and the repository layout.
2026-10-31 20:51:06 +00:00

13 KiB
Raw Permalink Blame History

Using the 1990 r5.01 (OS/2) TMS34010 toolchain

The 1990 TMS340 Family Code Generation Tools r5.01 are a strict superset of the 1987 DOS tools in 1987-05 TMS34010 SDK/, with one practical catch: they are 16-bit OS/2 1.x NE programs, not DOS. This file is the how-to: what they buy you, how to get them running today, and every command line that was verified on 2026-10-31.

Measured on one 110-line K&R C source (loops, array indexing, switch, struct, helper call, bit ops) compiled by both toolchains:

build .text instrs .obj
1987 GSPCG 2.01, no optimizer 704 B 243 1364 B
5.01, no -o 684 B (−2.8 %) 237 1298 B
5.01 -o0 550 B (−21.9 %) 202 1164 B
5.01 -o1 474 B (−32.7 %) 173 1038 B
5.01 -o2 452 B (−35.8 %) 161 1016 B

So: use -o2. Without an -o flag the newer compiler is only ~3 % better than the 1987 one — all the win is the optimizer (gspopt, an intermediate-code optimizer, not an assembly peephole). But 5.01 is also a language upgrade (ANSI C by default, -pk for K&R) and a tooling upgrade (single driver, ANSI headers, 104 vs 80 runtime symbols, 6 runtime variants).


1. What is in the package

1990-11 TMS340 Family SDK/
  1990-11-19 TMS340 Family Code Generation Tools (1 of 2) ...zip   # executables
  1990-11-19 TMS340 Family Code Generation Tools (2 of 2) ...zip   # headers + libs
  1990-11-19 TMS340 Family C Source Debugger r5.00 ...zip
  1990-11-19 TMS340 Family Graphics Library (1..3 of 3) r2.01 ...zip
  1990-11-19 TIGA SDK r2.01 ...zip

Disk 1 (the tools you need):

file role version banner
gspcl.exe C compiler driver (preprocess → compile → optimize → assemble → link) TMS340 C Compiler Shell 5.01
gspac.exe C parser / front end —
gspcg.exe code generator TMS340 C Codegen 5.01
gspopt.exe intermediate-code optimizer TMS340 ANSI C Optimizer 5.01
gspa.exe COFF assembler TMS340 COFF Assembler 5.01
gsplnk.exe linker TMS340 COFF Linker 5.01
gsprom.exe COFF → Tek-hex converter COFF Object Converter 5.01
gspar.exe, clist.exe archiver, listing utility

Disk 2: assert.h ctype.h errno.h float.h limits.h math.h setjmp.h stdarg.h stddef.h stdlib.h string.h time.h sysint.if, and the runtimes rts.lib rtsb.lib rtsc.lib rtscb.lib rtsi.lib rtsib.lib (integer/float variants) plus flib.lib flibc.lib flibi.lib, rts.src and flib.src.

Extract to a working directory:

cd "tools/tms34010-sdk/1990-11 TMS340 Family SDK"
unzip -o "1990-11-19 TMS340 Family Code Generation Tools (1 of 2) r5.01 rev2564062-1641.zip" -d ../../os2-501/bin
unzip -o "1990-11-19 TMS340 Family Code Generation Tools (2 of 2) r5.01 rev2564062-1641.zip" -d ../../os2-501/lib

They are MZ+NE, NE 5.1, target OS byte 0x01 (OS/2 1.x), and every tool imports only DOSCALLS — no Presentation Manager, no VIO/KBD, i.e. plain console programs. That is why they can work under an emulation layer as well as under real OS/2.


2. Getting a runtime

This is the only route that runs gspcl today.

Media: the preinstalled image on archive.org, item Os21.3VirtualHardDisk, file MicrosoftOs-21.30HardDisk.vhd (a Virtual-PC dynamic VHD, 500 MiB virtual). Convert it to raw:

qemu-img convert -O raw MicrosoftOs-21.30HardDisk.vhd os2-13.img

The image is one bootable FAT16 partition, CHS 1015/16/63, partition start LBA 63 — so the filesystem is editable from the host while the VM is stopped:

mcopy -i os2-13.img@@32256 myfile.c ::/TI/

86Box settings that work (86Box 6.0):

[Machine]
machine = asus386            ; AMI BIOS: accepts a user-defined (type 47) disk
cpu_family = i386dx
cpu_speed = 20000000
mem_size = 6144

[Video]
gfxcard = cl_gd5422_migrated_isa

[Storage controllers]
hdc_1 = ide_isa_2ch

[Hard disks]
hdd_01_fn = os2-13.img
hdd_01_ide_channel = 0:0
hdd_01_parameters = 63, 16, 1015, 0, ide
hdd_01_speed = ramdisk

Boot to [C:\] (~10 s). Two traps:

  • CONFIG.SYS and batch files must be CRLF. LF-only merges lines and the kernel dies with SYS1919 / an internal processing error.
  • This image does not run AUTOEXEC.BAT or STARTUP.CMD. Verified with a file-marker test. Put everything you need on the disk first, then start the work with one typed command.

If you drive 86Box over Xvfb with xdotool, note that the emulated keyboard drops keys typed faster than ~0.9 s apart, and punctuation/shift are unreliable. Type only letters (name your batch CG.CMD, BK.CMD, …) one character at a time, and have the batch print a unique end token so the host can detect completion by OCR (or just wait and take a screenshot).

2.2 2ine — assembler/linker/converter natively, no VM

gspa, gsplnk and gsprom run under 2ine with a small patch series; the patched fork lives at https://git.wut.ee/opencode.energia.arti.ee/2ine-tms34010 (see its TMS34010.md). Build it 32-bit (cmake -S . -B build && cmake --build build -j2, needs gcc-multilib, libc6-dev-i386, libncurses-dev:i386).

cd 2ine/build          # lx_loader dlopen()s ./lib<module>.so, so cwd matters
./lx_loader gspa.exe   test.asm                  # assemble
./lx_loader gsplnk.exe test.obj rts.lib -o test.out
./lx_loader gsprom.exe test.out test.hex         # COFF -> Tek-hex
./lx_dump  gspa.exe                              # NE header/segments/imports

gspcl (the C compiler) does not work yet there: the gspac child hits lxConvert1616to32 with a selector 2ine has not mapped. Use 86Box when you need to compile C.


3. The compiler driver: gspcl

gspcl [-options] filenames... [-z link_options...]

Verbatim option summary (from the tool's own usage output, VCL.TXT):

  -c      no linking (negates -z)    -q     quiet
  -dNAME  pre-define NAME            -qq    super quiet
  -g      symbolic debugging         -s     C source interlist
  -i<dir> #include search path       -uNAME undefine NAME
  -k      keep .asm file             -vXX   target processor 340XX
  -n      compile only (no asm)      -z...  link, options follow

Parser:     -pe -po -pk (K&R) -pw -pl -p? -pn
Typing:     -tf -tp
Runtime:    -ma -ms (small model) -mf (IEEE) -mn -mc (TMS34082) -me -mr -mx -mm
Optimizer:  -o (level 2) -o0 (register) -o1 (+local) -o2 (+global) -om (inline)
Assembler:  -al (listing) -ax (xref) -ah (old hex) -as (local symbols)
Files:      -fa -fc -fo -ft<dir>;  extensions: -ea<.ext> -eo<.ext>
Linker (-z): -a (absolute) -ar (relocatable) -c (ROM init) -cr (RAM init)
             -e<sym> -f<val> -h -i<dir> -l<lib> -m<map> -o<file> -r -s -u<sym>

Two things to internalise:

  • -o alone means level 2. -o0 is the lowest optimization level, not "off". With no -o* at all the optimizer is not run.
  • ANSI C is the default. int f(int a) compiles. -pk selects K&R compatibility for 1987-era sources.

Examples

Compile and link an absolute image in one step:

gspcl -k -o2 file.c -z -a -c -o file.out -m file.map -lrts.lib

Compile only, keep the generated assembly (this is what you want while debugging codegen):

gspcl -k -o2 -c file.c            -> file.obj, file.asm
gspcl -k -o2 -al -c file.c        -> ... plus file.lst (assembly listing)

K&R source from the 1987 era:

gspcl -pk -o2 -k -c file.c

Several C files plus a library:

gspcl -k -o2 -c a.c b.c
gspcl -o2 -z -a -o prog.out -m prog.map a.obj b.obj -lrts.lib

Environment variables: C_OPTION (default options), C_DIR (include/library paths), TMP (temp directory).

Manual pipeline (what the driver does internally)

gspac  -k file.c file.if        # parse -> intermediate
gspopt -k -O2 file.if file.opt  # optimize intermediate (optional)
gspcg  -m file.opt file.asm file.tmp
gspa   file.asm                 # -> file.obj
gsplnk ...                      # or let gspcl -z do it

On the benchmark source the .if is 3263 B, the .opt 4032 B (the annotated intermediate grows), and the object shrinks 1298 → 1016 B. Use the manual form only when you want to inspect or hand-tune the intermediate; the driver is otherwise equivalent (same 1016-byte object, same .asm except a header comment).


4. Assembler, linker, converter

GSPA   file.asm                     # -> file.obj
GSPLNK file.obj rts.lib -o file.out # simple form; or a command file (see below)
GSPROM file.out file.hex            # COFF -> Tek-hex

An assembly listing is produced by the driver's -al option (gspcl -al -c file.c); via the standalone assembler the same listing switch is passed as -l to GSPA.

Linker command file (rt.cmd), as used for the VT1200 ROM round-trip:

-a
-o file.out
-m file.map
file.obj
SECTIONS
{
  .text 3F208000h: {}
}

The SECTIONS origin is a bit address (TMS34010 is bit-addressed), and gsprom -i writes the high/low EPROM pair (same interface as the 2.01 converter used in the ROM round-trip). As with the 1987 converter, the hex file's own addresses wrap at 64 KB, so take placement from the COFF file or the map, never from the hex.

Assembler differences vs 2.00: 65 directives vs 51; new .ASECT .COPROC .ENDSTRUCT .EXE .HWORD .IEEEFL .LABEL .STRUCT .TAG .TMS340 .TMS34010 .TMS34020 .TMS34082 .VERSION. .usect and .if already existed in 2.00.

Compatibility is one-way. GSPA 5.01 assembles 2.00 output unchanged; GSPA 2.00 rejects 5.01 output (11 errors: .version, CLRS, bare JR). Upgrade the assembler together with the compiler.


5. Libraries and headers

library 1987 1990
rts.lib 41,922 B, 66 members, 80 symbols 67,784 B, 85 members, 104 symbols
flib.lib 12,686 B, 28 members, 40 symbols 15,512 B, 35 members, 47 symbols

1990 ships six integer runtime variants (rts, rtsb, rtsc, rtscb, rtsi, rtsib — byte/large-model variants) and three float variants (flib, flibc, flibi). Math moved into rts.lib (_sin, _sqrt, _pow, …), errno and strxfrm were added, and _printf was removed (only sprintf remains in rts.lib) — if you need formatted output, use sprintf.

Headers are ANSI-ised with prototypes and include guards (stdarg.h v5.01, stdlib.h, errno.h, math.h). Point -i at the disk-2 directory (and -i/-z -i at the library directory) if you keep headers/libs elsewhere.


6. Gotchas

  • -o is not optional if you want the win. No -o → no optimizer → only ~3 % smaller than 1987. -o2 → 36 % smaller.
  • Bit-addressed sizes. COFF section sizes are in bits (16 bits per instruction word); a .text of 0x0e20 is 452 bytes, not 3.6 KB.
  • ANSI by default. K&R code needs -pk; mixing old headers with the new compiler may need -tf/-tp (relaxed prototype/pointer checks).
  • CRLF for CONFIG.SYS and .CMD/.BAT inside the OS/2 guest.
  • AUTOEXEC.BAT/STARTUP.CMD are not executed by this OS/2 1.3 image — stage files offline through the disk image (mtools, offset 32256).
  • Typing into 86Box: ≥0.9 s per key, letters only; punctuation and shifted characters are unreliable. Name batches with letters (CG.CMD) and invoke them by bare name.
  • Don't put the 5.01 .asm through GSPA 2.00 — it will not assemble.
  • No /dev/kvm needed for 86Box; it is a pure software emulator.

7. Throughput (why this matters for the edit/compile loop)

One 2,412-line / ~2,000-instruction assembly, same file everywhere, measured 2026-10-31 on this host:

environment assembler s / run
2ine (native, 32-bit loader) 5.01 gspa 0.054
DOSBox-X 2025.02, cycles=max 2.00 GSPA 0.070
DOSBox 0.74, cycles=max 2.00 GSPA 0.104
86Box, 386DX @ 20 MHz, OS/2 1.3 5.01 gspa 4.50
DOSBox 0.74, cycles=3000 2.00 GSPA 5.06

So the full 5.01 toolchain is ~80× slower than 2ine's native assembler: use 86Box for compiling (where the 36 % code-size win is), and 2ine for iterating on assembly. Booting the OS/2 guest costs ~10 s, so keep it running while you work.


8. Reproducing the numbers

The comparison above comes from a K&R-compatible bench.c (sum loop, indexed loop, switch, struct update, helper call, bit loop) compiled five ways (2.01; 5.01 with no -o, -o0, -o1, -o2), with sizes read from the COFF section headers and instruction counts from the assembler listings. The exact commands are the ones in §3; the per-function table and the gspopt before/after are in /home/arti/os2-work/codegen/REPORT.md on the build machine (scratch, not part of this repo).