Skip to content

Latest commit

 

History

2 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

as400-doom

Doom on an IBM i (AS/400) green screen.

Two backends over one doomgeneric core:

  • as400doom — compiles in IBM i PASE and renders 24-bit colour Unicode half-blocks to your SSH session. Actually playable.
  • as400doom5250 — a TN5250E server. Point tn5250, tn5250j or IBM ACS at it and play Doom on a real 5250 green screen. No AS/400 required, which is the point: the interesting part is the protocol, and an IBM i is not something most people can borrow.
$ make && make wad
$ ./bin/as400doom                 # terminal
$ ./bin/as400doom5250             # then aim a 5250 emulator at localhost:2323

The thing everyone assumes is impossible

A 5250 is block-mode. The received wisdom is that you get roughly one frame per Enter key, which is why green-screen Doom is usually a joke rather than a project.

That is wrong, and it is worth being precise about why. A 5250 client is a pure slave for output: its receive loop drains every record that arrives and applies it immediately, with no "am I invited?" test anywhere in the path. The host can push unlimited unsolicited Write-To-Display records. You can stream video to a terminal designed in 1977.

Better still, the client calls its render function once after draining the whole queue — so a host that outruns the terminal gets automatic frame coalescing instead of an unbounded backlog. Free adaptive frame-skip.

Input is the half that really is block-mode: a 5250 cannot transmit spontaneously. Each AID keypress sends exactly one record. Video streams, keys trickle.

Getting 29 shades out of 7 colours

A 5250 attribute byte occupies a screen cell. Two adjacent cells cannot have different colours — every colour change burns a cell, which paints black. Seven colours, minus a cell per transition, sounds like a flat and speckled image. The first implementation here spent 47% of the screen on attribute cells.

Two things fix it.

The attribute picks intensity, not just hue. A normal attribute is a coloured glyph on black, so a density ramp (' ' '.' '*' '#') gives partial coverage; a reverse attribute with an EBCDIC space gives a solid block. That is 7 colours × 4 intensities + black ≈ 29 tones. Glyph changes are free; only attribute changes cost a cell. And level 0 is black under any normal attribute, so black — which is most of a Doom frame — never breaks a run.

Where the colour changes go is a shortest-path problem, not a per-pixel choice. At each column you either render under the current attribute or spend the cell on a switch. The key structural point is that the switch cost does not depend on which attribute you switch to, so the inner minimisation is one running minimum shared by all 14 states and the DP collapses from O(states²) to O(states) — 14 operations per column, ~50K per frame.

The result is the provably lowest-error assignment for each row, not a heuristic. Attribute cells per frame dropped 262 → 63, non-black cells rose 1423 → 1926, and frames got smaller (1113 → 851 bytes).

Status

ANSI / terminal backend works, verified
TN5250E server works, verified against a protocol-auditing client
PASE build wiring flags verified against IBM's toolchain; not run on real hardware
Native ILE C build written, never compiled — see ile/README.md

I do not have an AS/400. Everything marked verified was verified on Linux against real protocol implementations; everything else says so plainly.

Building

make            # both binaries
make wad        # fetch Freedoom (BSD-licensed) into ./wads
make test       # unit tests -- run these on the IBM i too
make pase       # run ON the IBM i, in a PASE ssh session

On an IBM i

yum install gcc12 make-gnu coreutils-gnu
gmake pase && gmake test

Or push and build in one step:

tools/deploy-pase.sh you@your-ibmi

make pase uses gcc-12, not gcc. The plain gcc on IBM i is a symlink to GCC 6.3.0, which IBM has abandoned and which fails on 7.5+ with spurious errors out of sys/socket.h. It also passes -Wl,-bbigtoc, without which AIX's 64KB Table of Contents overflows at link time on a codebase this size. See docs/PORTING.md.

Playing

Terminalw/s move, a/d strafe, q/e turn, arrows, space to use, Ctrl-L fire, Esc menu, Tab automap.

5250 — a 5250 only transmits on AID keys, so everything is a function key: F1/F2 forward/back, F3/F4 turn, F5 fire, F6 use, F7/F8 strafe, F9 automap, F10 menu.

Neither a terminal nor a 5250 can report key release, so held keys are inferred from autorepeat — see the long comment in src/input.h for why movement stutters once on first press and why that is inherent.

variable
AS400_DOOM_COLOR truecolor (default) / 256 / ascii
AS400_DOOM_FPS draw-rate cap; 35 terminal, 20 for 5250, 0 uncapped
AS400_DOOM_KEYHOLD_MS synthetic key-release window (default 300)
AS400_DOOM_PORT TN5250E listen port (default 2323)
AS400_DOOM_5250_WIDE force 24×80 (0) or 27×132 (1)
AS400_DOOM_5250_FULLWIDTH use all columns; more detail, stretched

The draw-rate cap is decoupled from the game tick — it drops frames, not tics, so the simulation stays exact. It matters: uncapped truecolour at 100×30 sustains ~1.3MB/s of escape codes, which is fine on a local pty and hopeless over SSH to someone else's datacentre. At 10fps it is 359KB/s.

Tools

  • tools/t5250_probe.py — a TN5250E client that renders the session to PNG and audits the protocol. The reference C client frames purely on IAC EOR and never reads the record length field, so a malformed header passes local testing and only fails later against IBM ACS. This checks it.
  • tools/ansi2png.py — replays an ANSI capture into a PNG. The terminal output is diffed, so you cannot eyeball a single frame; you have to replay.
  • tools/deploy-pase.sh — rsync to an IBM i, build, run the tests there.

Layout

src/
  dgcompat.h      pixel extraction, char-signedness assert, geometry
  render.c        box-filter 320x200 -> character-cell grid (shared)
  input.c         key queue with synthesised key-release (shared)
  wadfind.c       IWAD autodetect -- doomgeneric compiles out the engine's
                  own search, so DOOMWADDIR is silently ignored
  ds5250.c        the 5250 encoder: DP colour assignment + order emission
  backend_ansi.c  DG_* for a terminal
  backend_5250.c  DG_* + the TN5250E server
  dgassert.c      compile-time sizes for every on-disk WAD struct
vendor/doomgeneric/   engine, with 4 documented patches (docs/PORTING.md)
ile/                  native ILE C path -- researched, unverified

Licence

GPL-2.0-or-later, inherited from Doom via doomgeneric. LICENSE is the verbatim GPLv2. Doom is © 1993–1996 id Software; doomgeneric is © ozkl; Chocolate Doom lineage © Simon Howard.

Freedoom (fetched by make wad, not committed) is BSD-3-Clause and is not covered by this repo's licence. Commercial Doom IWADs are not redistributable — if you own Doom, pass -iwad /path/to/doom.wad.

About

Doom on an IBM i (AS/400) green screen. PASE/ANSI backend + a TN5250E server so you can play on a real 5250 with no AS/400.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages