FujiNet Programming Guide for the Atari 5200 - FujiNetWIFI/fujinet-firmware GitHub Wiki
FujiNet Programming Guide for the Atari 5200
The FujiNet programming manual for the Atari 5200: the memory map and the
mailbox the console reaches by reads alone, the text display three ways, living
in 16K, the 6502 library and C with fujinet-lib, the controllers, ten worked
programs in both languages, the command reference, display lists and players,
and walkthroughs of Battleship, 5 Card Stud and CONFIG. This is the wiki
condensation; the full illustrated edition is the PDF in
atari-5200/programmers-handbook/ of the manuals repository, styled after the
1982 ATARI 5200 Owner's Manual.
See also the FujiNet-Network-Protocol-Handbook
for everything the N: device can say once you can reach it.
Contents
- Your FujiNet cartridge
- Installing the tools
- The memory map
- How the mailbox works
- The text display
- Living in 16K
- The libraries
- Controllers, keypad and sound
- The programs
- Booting another image
- Banks and the Super Cart
- Command reference
- Battleship, 5 Card Stud and CONFIG
- Things that cost real time
- Troubleshooting
- Sources of truth
Your FujiNet cartridge
An RP2354B on the cartridge edge answers every read the console makes of the
cartridge, as a ROM would, through a 74HCT541 that only the console's own
cartridge enables can open. Behind it, over USB, an ESP32-S3 runs the FujiNet
firmware (fujiversal-atari5200).
The console sees CONFIG at power-on, a mailbox at $B000-$B7FF, and up to
128K of program served as a plain ROM, a two-chip board, Bounty Bob Strikes Back
or a Super Cart.
The board is a Rev0 design that has not yet been built. Everything here ran in host tests and the MAME cartridge model. The edge has no reset line and no read/write line; the RESET key is a keypad key.
Installing the tools
- cc65 2.19:
ca65,ld65,cc65,cl65. cc65'satari5200target is the C library. - fujinet-lib with the
atari5200target:tschak909/fujinet-lib, branchadd-atari5200(FujiNetWIFI/fujinet-lib PR #139) until it merges;make atari5200. Its 5200 headerfujinet-atari5200.his inbus/atari5200/: put that on the include path as well asinclude/. - MekkoGX, branch
add-atari5200(FozzTexx/MekkoGX PR #27): the start-up codeatari5200-crt0.s, the linker configurationatari5200-fujinet.cfgand the checkeratari5200-romstamp.py. - MAME with the cartridge:
fujinet-firmware/pico/atari-5200/emu/apply.sh mame, thenmake SUBTARGET=a5200 SOURCES=src/mame/atari/atari400.cpp REGENIE=1; run with-cartslot fujinet. MAME must run from its own tree for-autoboot_script. The cartridge dialsFUJINET_TCP(default127.0.0.1:9995). - fujinet-pc, the RS232 build (
./build.sh -p RS232), from firmware new enough to know the.a52extension.
There is no FujiNet Go for the 5200.
The memory map
| Address | What |
|---|---|
$0000-$3FFF |
RAM, 16K |
$4000-$BFFF |
the cartridge |
$C000 |
GTIA |
$D400 |
ANTIC |
$E800 |
POKEY |
$F800-$FFFF |
the BIOS |
In the cartridge window a FujiNet program (an APP) has $4000-$AFFF and
$B800-$BFDF; the mailbox is $B000-$B7FF; the claim and the BIOS header
are $BFE0-$BFFF:
| Address | What |
|---|---|
$BFE0 |
"FUJI", version 1, board + 1 (0 = from the size), flags 0 |
$BFE7 |
$02: PAL-compatible, for the two-port BIOS |
$BFE8 |
20-character title, unseen |
$BFFD |
$FF: skip the BIOS title screen |
$BFFE |
start address |
MODE at $B412: 0 BOOT (CONFIG), 2 GAME (no claim, no mailbox), 3 APP.
Never store into $4000-$BFFF; never use a read-modify-write instruction
there; read the hotspot pages only with LDA/LDX/LDY/BIT absolute or
absolute-indexed from a page-aligned base; never read one hotspot twice in a
row; never JMP ($xxFF); never point ANTIC at $B000-$B7FF.
How the mailbox works
With no read/write line, everything the console sends travels in the address of a read:
| Address | Read it to |
|---|---|
$B000-$B3FF |
learn: the reply, up to 1K |
$B400-$B4FB |
learn: the status page |
$B500+n |
say: select register n |
$B600+v |
say: set the selected register to v |
$B700+b |
say: append the byte b to the TX stream |
Registers: $00 DEVICE, $01 CMD, $02 NPARAM, $05 DATA_RST, $06
RXSLICE, $10 SEQ, $11 BOOTLOCK ($B5), $12/$13 BOOTSEL ($B5, $4A),
$14 CONFIG ($C5).
ldx #FN_REG_CMD
lda FN_REGSEL,x ; select register $01
ldy #$C4 ; GET_ADAPTERCONFIG_EXTENDED
lda FN_REGDATA,y ; ...and set it
The TX stream is NPARAM × {size 1/2/4, value low first}, then the payload; 320
bytes, at most 8 parameters. Launch with SEQ = ACKSEQ + 1 (255 wraps to 1);
the cartridge paints the reply, ERR ($B402), REPLYCMD ($B403, $06
ACK) and RXLEN ($B404), and copies SEQ into ACKSEQ ($B400) last. The
count starts over at 0 at every power-on and hand-over. Budgets: 5 s, 60 s for
MOUNT_IMAGE and COPY_FILE, 90 s for the network device's OPEN/CLOSE/STATUS/READ.
The text display
ANTIC mode 2, 40 × 24, from a 1K-aligned font in ROM. fujidisp.s (assembly,
screen at $1000) and CONFIG's antic.c (C, screen at $3C00) draw the same
screen from the same font. cc65's own conio is a 20 × 24 mode-6 screen at
$3E20: link with __RESERVED_MEMORY__ of at least 0x1E0, or the C stack
lands on it. Set the BIOS shadows (SDLSTL/H $05/$06, SDMCTL $07,
COLOR0-4 $0C-$10); the VBI copies them.
Living in 16K
Zero page $00-$18 is the BIOS's layout (POKMSK, RTCLOK, CRITIC, ATRACT,
display and colour shadows, PADDL0-7); both start-up files keep it. Vectors at
$0200-$021B. Assembly: KEY at $19, the library at $E0-$ED, BSS from
$0300, the screen at $1000; a52.cfg's ZEROPAGE starts at $19, on top of
KEY, so put variables in BSS. C: zero page from $1D, RAM from $021C, a 1K
stack under the reserved top. A hand-over clears all of RAM; keep what must last
in an appkey.
The libraries
Assembly (the bring-up's testrom/): fn_beg (DEVICE/CMD/NPARAM from
FN_DEV/FN_CMD/FN_NPR, rewinds TX, FN_TMO = 12 s), fn_pb, fn_pw,
fn_txb (takes its byte in Y: count string loops in X), fn_path (256-byte
padded), fn_go (launch and wait, frames counted by the VBI; returns ERR or
$FF), fn_ack ($EE on NAK), fn_boot, fn_config.
C: fujinet-lib's usual fuji_*, network_*, clock_*, plus
fujinet-atari5200.h: fuji_a5200_present(), fuji_a5200_boot_state() and
friends, fuji_a5200_boot(), fuji_a5200_config(), fuji_a5200_key,
fn_default_timeout, fn_device_error. Below it, fn_regwr(), fn_tx(),
fn_commit(). cc65's Atari targets compile '\n' as $9B: spell out
0x0A/0x0D. cc65 drops an inline lda whose value is unused: switch banks in
assembly.
Controllers, keypad and sound
| Part | Read from |
|---|---|
| Stick | PADDL0 (X) / PADDL1 (Y) for controller 1, 114 at rest |
| Bottom (fire) button | GTIA TRIG0 $C010, 0 while held |
| Top button | POKEY SKSTAT bit 3 $E80F, clear while held |
| Keypad | POKEY KBCODE by interrupt: KEY / fuji_a5200_key = code + 1 |
Key codes: 0-9, $0A *, $0B #, $0C START, $0D PAUSE, $0E RESET.
Write $04 to CONSOL ($C01F) before reading the stick: bit 2 turns the
pots on, nobody else sets it, and MAME does not model it. The keypad runs with
POKEY's debouncing off, so a held key interrupts on every scan: count it once,
then ignore it for four frames. POKEY sound: AUDF1/AUDC1 at
$E800/$E801, $A8 a pure tone at volume 8.
The programs
Ten programs, each in ca65 assembly and in C, all verified in MAME against a
running fujinet-pc (emu/verify.sh, 20/20):
| Program | Shows |
|---|---|
| hello | the screen, the frame counter, the keypad, the cartridge's magic |
| first contact | GET_ADAPTERCONFIG_EXTENDED: SSID, IP, firmware |
| pads | stick, both buttons, keypad, a POKEY tone |
| netget | an HTTP GET through N: |
| netcat | a TCP conversation, with an on-screen keyboard |
| dir | a directory on the SD card |
| boot | push an image into the cartridge and hand over |
| appkey | the shared player name, and a key of our own |
| clock | local time and UTC |
| bank | a 64K Super Cart: transactions from both banks |
Booting another image
MOUNT_HOST $F9, SET_DEVICE_FULLPATH $E2 (device, host, mode, 256-byte path),
MOUNT_IMAGE $F8 (60 s): the FujiNet pushes the file (.a52 or .bin; a
.cfg sibling's mapper= line chooses the board). Watch BOOTSTATE $B406
(2 ready, $80 failed, BOOTERR $B408 says why) and BOOTPCT $B407. Then:
BOOTLOCK = $B5, wait for ARMED $B41C, quiet the machine (interrupts, DMA,
chips and RAM to 0), JMP $B5F0: the cartridge serves JMP ($FFFC) there and
swaps the image in on the read of its last byte, so the BIOS starts it as at
power-on. fn_boot / fuji_a5200_boot(). Back to CONFIG: register $14 =
$C5, then the same hand-over (fn_config / fuji_a5200_config()).
Banks and the Super Cart
64K or 128K in 32K banks, switched by reads: $BFE0-$BFFF the last bank (the
power-on bank), $BFD0/$BFD4/$BFD8/$BFDC banks 0-3. Use BIT. The claim at
$BFE0 is itself a hotspot. Keep everything that runs or is read by ANTIC
during a switch identical in every bank: the handbook's programs give each bank
only $A000-$AFFF of its own.
Command reference
The FUJI device $70 (54 commands), the network devices $71-$78 (29),
disk $31-$38, printer $40, clock $45 and modem $50, each with a card
and examples in both languages, checked against the firmware's dispatch tables
by tools/cmdcheck.py. See the PDF.
Battleship, 5 Card Stud and CONFIG
Both games are one program for every client with a 5200 layer
(src/atari5200/): ANTIC mode 4 in the top 4K of RAM
(__RESERVED_MEMORY__=0x1000), input folded into key codes, POKEY sound,
fujinet-lib for the network and appkeys. The server address comes from appkey
creator 1, app 1, key 5 (Battleship) or key 1 (5 Card Stud), as server?query.
CONFIG (fujinet-config/atari5200/) is a 32K APP in C baked into the cartridge;
it launches MOUNT_IMAGE without waiting so its progress bar moves during the
push. The games and CONFIG currently build against fujinet-lib-experimental's
atari5200 target.
Things that cost real time
- A held keypad key counts many times (debouncing off).
- The stick needs
CONSOLbit 2; MAME does not care. - No read/write line: a store into the window fights the bus.
- Two reads of one address in a row can merge on the four-port console.
- An old fujinet-pc does not know
.a52, says yes to MOUNT_IMAGE and pushes nothing. fn_txbtakes its byte in Y.- cc65 reads
'\n'as$9B. a52.cfg's zero page starts on top of KEY.- cc65's conio screen is where the C stack goes.
- fujinet-lib #139's 5200 header is outside
include/.
Troubleshooting
Check ERR ($B402) and REPLYCMD ($B403) first. ERR 1: no link. ERR 2:
the FujiNet took longer than the budget. ERR 3: a parameter size other than
1, 2, 4. $FF from fn_go: the hotspot reads went wrong, or SEQ repeated. NAK:
the command was refused. BOOTSTATE stuck at 0: not a cartridge image, or an old
fujinet-pc. No mailbox after a boot: no claim, or a two-chip/Bounty Bob image.
Sources of truth
| What | Where |
|---|---|
| Cartridge, library, test programs | fujinet-firmware pico/atari-5200, pico/common @ 7a53805c8 |
| Command dispatch, fujinet-pc | fujinet-firmware @ f0b62cb09 |
| fujinet-lib atari5200 | tschak909/fujinet-lib add-atari5200 @ 7cf47dc (PR #139) |
| Start-up, cfg, romstamp | tschak909/MekkoGX add-atari5200 @ faeb3fd (PR #27) |
| CONFIG | fujinet-config main @ 1bd6ec8 |
| Battleship | fujinet-battleship main @ 3f2a548 |
| 5 Card Stud | fujinet-5cardstud main @ d5db857 |
| cc65 | V2.19 92fa7cb23 |