An Air Traffic viewer for Atari 8-bit computers, powered by FujiNet.
Tested on real hardware: stock 130XE and 800XL with GTIA2RGB FPGA companion board.
- Live worldwide air traffic on a PPI radar scope - up to 15 aircraft, stable target numbers (slots keyed by the icao24 airframe id), nearest first, ground traffic distinguished from airborne.
- Colour-coded altitude bands on GTIA2RGB (80x23, per-cell colours, icon font with real directional aircraft glyphs), monochrome GR.0 rendering on stock video.
- Range zoom 5-120 nm, fading trails, range rings, centre-airport runway overlay with true headings and lengths.
- Follow mode: lock the scope onto one aircraft and it stays centred as it flies, so it never slips off the edge of range.
- Target selection with a detail block: callsign, type, squawk, exact altitude with climb/descend trend, ground speed, track, distance and 16-wind bearing from the centre.
- Emergency squawks (7500/7600/7700) in blinking red + POKEY two-tone alarm; contact gained/lost beeps (mutable).
- METAR for the centre station on the bottom row.
- 4 location profiles stored on the FujiNet (appkeys), centre by ICAO code (resolved online, runways fetched along) or raw lat/lon.
- Two data sources: direct (adsb.fi, zero infrastructure - the default) or a lightweight fujiatc-server relay (~5 s refresh, sub-kilobyte polls); see Data sources.
Each release ships four builds. They all run on any XL/XE - a wrong pick is harmless (no FPGA detected -> standard 40 columns). The split selects the display driver and reserves extended-RAM support:
| File | Best for | Display |
|---|---|---|
fujiatc-xeg2r.xex |
130XE (128 KB) + GTIA2RGB | 80-col colour + rainbow |
fujiatc-xe.xex |
any 128 KB machine, stock video | 40-col GR.0 |
fujiatc-xlg2r.xex |
64 KB machine + GTIA2RGB | 80-col colour + rainbow |
fujiatc-xl.xex |
64 KB machine (800XL/65XE/1200XL) | 40-col GR.0 |
Rule of thumb: have a GTIA2RGB? take a *g2r build. Have 128 KB?
take an xe* build. Otherwise fujiatc-xl.xex. (The 128 KB banks are
reserved for upcoming replay/cache features.)
- Atari XL/XE with 64 KB RAM
- FujiNet with a recent firmware (developed and tested against 1.6.1), configured WiFi, and an SD card if you want the configuration to survive a power cycle (appkeys live on SD)
- optional: GTIA2RGB FPGA extension for 80-column colour
- internet access from the FujiNet (both sources are reachable over HTTPS; no accounts, no API keys)
- Boot
fujiatc-*.xex(FujiNet CONFIG, SIDE, your favourite loader). - The radar starts around the built-in default location. Press OPTION, then E, type your airport's 4-letter ICAO code and RETURN - the coordinates and runway layout are fetched online.
- Press S to save, ESC to return. Done - traffic appears within one poll (~10 s).
| Key | Action |
|---|---|
+ / - |
zoom in / out (range 5/10/15/20/30/40/60/80/120 nm) |
R |
refresh now |
| SPACE | select next target (nearest-first order) |
L |
follow: lock the scope onto the selected aircraft |
1-9 A-F |
select the target with that slot number |
0 |
clear selection |
M |
sound on/off |
| OPTION | locations & configuration screen |
HELP or ? |
help page |
| SELECT at boot | force 40 columns this session (emergency override) |
A * in the status corner means an SIO transfer is in progress (input
is queued during it); the spinning arrow means idle time between polls.
Four profiles, A-D. Each holds a label, coordinates, range and the centre airport's runway geometry.
| Key | Action |
|---|---|
A-D |
activate a profile (or park the cursor on an empty slot) |
E |
set the active profile from an ICAO code - coordinates and runways resolved online (aviationweather.gov) |
L |
set it from raw lat/lon (decimal degrees, e.g. 50.0777 19.7848), then a label |
N |
cycle source: DIRECT (adsb.fi) / SERVER / RECEIVER (128 KB only) |
H |
server or receiver host (empty = default public server) |
V |
40/80 column video mode (GTIA2RGB only; switches live) |
S |
save everything to FujiNet appkeys |
| ESC / any console key | back to the radar |
Settings persist only after S (and need an SD card in the FujiNet).
DIRECT (default). The Atari talks straight to
adsb.fi open data over N:HTTPS and scans the JSON
body itself; airport/METAR lookups go to aviationweather.gov. Zero
infrastructure, ~11 s refresh (deliberately gentle toward the free
community API).
SERVER. A tiny relay (server/) fetches and caches adsb.fi and serves a compact ~0.5 KB binary blob instead of multi-kilobyte JSON. The Atari polls it every ~5 s: fresher picture, denser trails, snappier UI, and a fraction of the WiFi airtime.
A public instance runs at fujiatc.rustybits.pl - it is the
built-in default, so switching the source to SERVER (N, then S) is
all it takes. Fair use welcome; note the instance logs client IP
addresses for abuse protection (see the privacy note in
server/README.md). To run your own, see the same
README - one Python file, stdlib only, docker-compose optional. Enter
its address with H: a bare hostname is spoken to over HTTPS, a
host:port form over plain HTTP (handy on the LAN).
RECEIVER (128 KB machines only). Point the Atari straight at a
local dump1090-fa / readsb / tar1090 aircraft.json over plain HTTP
on the LAN - no server, no internet. Set the source to RECEIVER (N)
and the host with H (a bare IP gets /data/aircraft.json appended;
a host with a path is used as-is). 64 KB machines don't carry this
code - they use the server's receiver source instead (see
server/README.md).
All sources fill the same aircraft model - every screen feature works identically with any of them.
fujinet-lib over the N: device. The traffic poll opens one HTTPS GET
and consumes the raw body through a ~300-byte streaming JSON
scanner (direct mode) or a fixed-layout binary blob (server mode). The
FujiNet firmware's JSON parse/query channel is deliberately NOT used
for traffic: on real SIO it proved unreliable for big documents (the
scalar-query path is fine and still serves the airport resolver).
Rules inherited from FujiGPT and re-verified on FujiATC's own hardware tests:
- Never read more than STATUS reports waiting. A read from an empty N: buffer is NAKed (error 144) on real hardware.
- No WSYNC-heavy DLI during a long SIO data frame - it delays SERIN long enough to drop a byte at 19200 baud, the OS silently retries, and FujiNet reads are not idempotent. FujiATC's 40-column screen gates its rainbow DLI off around transfers; the 80-column Mode 3 screen needs no DLI at all (colour is data), which kills the whole conflict.
- Rotate N: units between requests (N1..N4) so an aborted transfer's leftover bytes can never bleed into the next one.
- Sample the keyboard between chunks - a pending key ends the body scan early; the slot TTLs cover the missed remainder.
- 80 columns (GTIA2RGB): COL80 Mode 3 - per-cell chroma+luma
attributes, the full Atari palette per character. The FPGA is probed
at boot (
$D01Ereturns$0A), the OS charset is uploaded in ATASCII order and overlaid with the radar's icon glyphs (aircraft arrows in 8 headings, trail dots, runway segments). Every mode F line carries its own LMS and the layout skips the 4 KB ANTIC boundary, so no wrap hazards. - 40 columns: direct writes to the OS GR.0 screen; on GTIA2RGB the HRBICOLOR override gives hi-res text real colour and the rainbow title DLI works FujiGPT-style.
- The keyboard is read via the K: handler vector directly - cc65's
conio cursor handling corrupts screen bytes through
OLDADR.
FujiNet appkeys, creator id 0x484A 'HJ', app id 0x02 (FujiGPT
holds 0x01 - the two coexist). Key 0: header (active profile, video
mode, source). Keys 1-4: one profile each (label, lat/lon, range,
runway geometry). Key 5: server host. All profiles are held in RAM;
S writes everything.
| Region | Contents |
|---|---|
$0480-$06FF |
page 4-6 scratch: devicespec, parse fields, METAR |
$2000-$9640 |
program + C stack |
$9800-$AF53 |
COL80 Mode 3 screen + display list |
$B000-$B7FF |
loaded constants (icons, tables) + network buffer |
$B800-$BC1F |
aircraft table, config, trails |
$BC20+ |
OS GR.0 screen (the 40-column fallback, kept intact) |
The 64 KB budget is essentially full - that is what the xe* banks
are for, next.
Windows: build.bat <variant>, Linux/macOS: ./build.sh <variant|all>
with xeg2r / xe / xlg2r / xl. Needs cc65
(CC65_HOME or on PATH); fujinet-lib
4.11.2 downloads automatically on first build. Output lands in bin/.
See server/README.md: endpoints, deployment (bare Python / docker compose / docker compose behind Traefik), environment variables, the private dashboard and the privacy note.
- Follow mode (lock the scope onto one aircraft) + flight routes
- Own ADS-B receivers as server sources (readsb
aircraft.json, SBS) - 128 KB features: traffic replay, map/zoom caches
- Coastline map layer (server-side, Natural Earth)
Credits for original idea go to k6lcm - check out his nes-radar and c64u-radar
Traffic data: adsb.fi community feeders. Airport, runway and METAR data: aviationweather.gov. Network/display groundwork: FujiGPT (same author).
HanJammer / Rusty Bits 2026


