gspac/gspopt/gspcg now run, so the status table and examples cover the full C path. gspcl itself still stops after gspac because the children exit 139 at teardown; the manual pipeline is the working route. Note the relative-path and consumed-intermediate requirements, the b15/sp round-trip difference, and the non-zero exit statuses.
6.2 KiB
2ine fork: TI TMS34010 OS/2 toolchain support
This is a fork of icculus/2ine at commit
1ca194f ("viocalls: Actually, VioGetMode() needs to initialize ncurses",
2022-01-31) plus a small patch series that makes TI's 1990 "TMS340 Family
Code Generation Tools r5.01" usable on Linux.
Those tools (gspa, gsplnk, gsprom, gspcg, gspopt, gspcl, …) are
16-bit OS/2 1.x NE executables that import only DOSCALLS — exactly the
shape 2ine's 16-bit bridge targets.
Status (verified 2026-11-01)
| tool | result |
|---|---|
gspa (TMS340 COFF assembler) |
works — assembles an 11,852-line disassembly of the VT1200 firmware region, "No Errors, No Warnings", valid COFF |
gsplnk (linker) |
works — links .obj + rts.lib/flib.lib into an absolute image |
gsprom (COFF → Tek-hex) |
works — both -i EPROM pair and plain Tek-hex |
gspar (archiver) |
works — lists rts.lib members |
gspac / gspopt / gspcg (C front end, optimizer, codegen) |
work — the null-selector fix below unblocks the C compiler |
gspcl (driver) |
partial — runs gspac, then stops; gspac/gspcg exit 139 at teardown. Drive the pipeline manually. |
Example session (from the repo root):
cd build # lx_loader dlopen()s ./lib<module>.so, so cwd matters
./lx_loader /path/to/gspa.exe test.asm
./lx_loader /path/to/gsplnk.exe test.obj rts.lib -o test.out
./lx_loader /path/to/gsprom.exe test.out test.hex
The C pipeline (front end → optimizer → codegen → assembler → linker):
./lx_loader /path/to/gspac.exe -k file.c file.if
./lx_loader /path/to/gspopt.exe -k -O2 file.if file.opt
./lx_loader /path/to/gspcg.exe -m file.opt file.asm file.tmp
./lx_loader /path/to/gspa.exe file.asm
./lx_loader /path/to/gsplnk.exe file.obj rts.lib flib.lib -o file.out
./lx_loader /path/to/gsprom.exe file.out file.hex
Pass relative filenames and keep the sources, objects and libraries in the
loader's cwd: the OS/2 tools cannot open Unix absolute paths, and the tools
consume their intermediate .if/.opt files, so regenerate them per run.
TMS340 COFF Assembler Version 5.01
(c) Copyright 1985, 1990 Texas Instruments Incorporated
PASS 1
PASS 2
No Errors, No Warnings
lx_dump also parses the NE headers (segments, fixups, DOSCALLS imports).
Build
Dependencies (Debian/Ubuntu names):
sudo apt install build-essential cmake libncurses-dev libncurses-dev:i386 \
libc6-dev-i386 gcc-multilib
Arch names (verified 2026-11-01):
sudo pacman -S --needed base-devel cmake lib32-glibc lib32-gcc-libs \
lib32-ncurses sdl2-compat
2ine builds 32-bit (-m32) and links the 32-bit libSDL2-2.0.so.0 that is
checked into the repo root. On CMake 4.x, pass the policy floor explicitly:
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug \
-DCMAKE_POLICY_VERSION_MINIMUM=3.5
cmake --build build -j2
That produces build/lx_loader (32-bit), build/lx_dump and the lib*.so
modules.
Patches
CMakeLists.txt— the hard-codedinclude_directories("/usr/local/include/SDL2")is used only if it exists; otherwise fall back to/usr/include/SDL2. Without this,lib2ine.ccannot findSDL.hon a stock distro.native/doscalls*.c/.h— 16-bit entry points these tools import:Dos16GetDateTime(ordinal 33). The OS/2 1.x 16-bitDATETIMEis the hours-first 10-byte layout (same shape as the 32-bit struct), not the year-first 8-byte one. Proven against the tool's ownasctime/localtimewrapper; getting this wrong crashesgspaat startup.Dos16NewSize(68) — validate selector, no-op (2ine maps full 64 KB).Dos16GetHugeShift(135) → 3,Dos16HugeIncr(136) — huge-segment selector arithmetic.Dos16CreateCSAlias(43),Dos16DevConfig(52),Dos16Move(67) — for the C compiler child.
lx_loader.c— NE fixup chains were walked only up to the segment's file size, so fixups whose target lies in the BSS tail were skipped, leaving the0xFFFF:0000placeholder in memory (gspathen segfaulted in alcallto its CRT exit handler). Chains now run to the 64 KB virtual size or the end-of-chain marker.native/doscalls.c—Dos16GetPIDasserted that the host PID fits in 16 bits. On modern kernels (pid_max= 4194304) it does not, so the process aborted before the tool ran. The 16-bit API already truncates; the asserts are disabled.lx_loader.c—lxConvert1616to32treated only0as null. The C tools pass0003:0000(selector index 0 with RPL 3), which is the OS/2 null far pointer; it hitldt[0]and aborted. Segment index 0 now returnsNULL. This unblocksgspac,gspoptandgspcg.CMakeLists.txt—HAVE_CURSES_Hfallback for CMake 4.x whenFindCursesleavesCURSES_HAVE_*unset, and a/usr/lib32fallback for the ncurses library when the Debian/usr/lib/i386-linux-gnupath is absent.
Known issues / next steps
gspcl(the driver) runsgspac, then stops:gspacandgspcgexit139at teardown even though they write correct output, andgspclgives up on the child status. Drivegspac → gspopt → gspcg → gspamanually; that pipeline produces a correct image.gspa5.01 rejects the B-file nameb15(INVALID REGISTER VALUE). The barespencodes the A15 namespace, while the 1987 assembler acceptedb15and encoded the B15 namespace. SP is the same physical register as A15 or B15, so the result is semantically identical but a firmware round-trip differs in one byte. See the VT1200tools/tms34010-sdk/2INE-501.md.- Huge-segment selector arithmetic is stubbed; nothing in the assembler path needs more than 64 KB of data.
- The tools commonly return a non-zero exit status (segfault or
1) after successful work. Check the output files rather than the exit code.
Provenance
Upstream: https://github.com/icculus/2ine. The patch series was written while bringing up the TI toolchain for the VT1200 project and is published here so the fixes are not lost; it has not been sent upstream.