Cross-Platform Amiga and Atari ST C Programming (“Not Conio But Compatible”)

Written on 07/19/2026
Chris Garrett

So far in this combined Amiga + Atari ST mini-series we’ve built up the basics of writing C with vbcc for both machines in parallel:

  1. Hello World with vbcc: the toolchain, the first build, and the in-browser IDE that runs both targets.
  2. Counter loops and game ticks: for, while, and how a loop becomes a frame.
  3. An ASCII table for dungeon tiles: turning glyphs into map data the C way.
  4. Reading keyboard input: getting a keypress out of the console on both platforms.

We now have enough C and enough console plumbing to start writing something that looks like a tiny roguelike. Almost.

The problem now is that the toy printf/getchar loop we’ve been using won’t work for a real game. We need a layer between the game and the machine (an adapter) and to save us rewrites, that layer needs to look the same to the game code on both the Amiga and the Atari ST.

▶ Try this lesson in your browser

This post is the shared theory behind two parallel platform posts. After reading, jump into either the Amiga or Atari ST adapter and run the finished code as a preset in our in-browser IDE, no SDK install, no toolchain setup:

Amiga 500 (AROS): open retroc-dungeon.c
Atari ST (EmuTOS): open retroc-dungeon.c

The problem: no conio.h

On DOS and on a handful of 8-bit C compilers you get conio.h: gotoxy, cputc, textcolor, kbhit. It isn’t standard C, but it’s small and fast, and it covers what a text-mode roguelike needs.

On the Amiga and on the Atari ST, with vbcc, there’s no conio.h. You get standard C (printf, getchar, putchar) plus each platform’s native APIs (AmigaDOS / Intuition on one side, GEMDOS / VT52 / XBIOS on the other). Neither of those native API sets looks anything like the other, and neither of them looks anything like conio.

So we build our own. Not a faithful port of Borland’s conio.h, but a smaller, focused library that gives the game code one tidy API and hides the platform mess underneath. We’ll call it platform.h.

The platform.h contract

The contract is the same header on both platforms. The game code includes platform.h, calls these functions, and never has to know whether it’s talking to AROS, EmuTOS, or a real Amiga or real ST:

/* platform.h — the same on Amiga and Atari ST */
#ifndef PLATFORM_H
#define PLATFORM_H

#include <stdint.h>

typedef uint8_t glyph_t;

/* lifecycle */
void  plat_init(void);
void  plat_shutdown(void);

/* drawing */
void  plat_clear(void);
void  plat_putc(uint8_t x, uint8_t y, glyph_t g, uint8_t colour);
void  plat_puts(uint8_t x, uint8_t y, const char *s, uint8_t colour);
void  plat_flush(void);

/* keyboard */
int   plat_kbhit(void);
int   plat_getch(void);

/* timing and randomness */
void  plat_sleep_ms(uint16_t ms);
uint32_t plat_ticks_ms(void);
uint32_t plat_seed(void);

/* screen metrics */
uint8_t plat_cols(void);
uint8_t plat_rows(void);

#endif

Fifteen functions. Lifecycle, drawing, keyboard, timing, metrics. That’s the whole surface the game gets to use. Everything else (cursor positioning, palette fiddling, raw-mode console input, escape sequences) lives behind these symbols, in a per-platform .c file.

How each adapter is structured

Both the Amiga and Atari ST adapters are organised into the same five sections, in the same order. If you read one and then the other, the headings line up and you can compare the platform-specific code side by side:

  • Glyph table: a tiny lookup that turns logical tile IDs (TILE_WALL, TILE_FLOOR, TILE_PLAYER) into the actual byte to send to the console.
  • Init and shutdown: set up the screen on the way in, put it back the way we found it on the way out. This is the most platform-specific section by a wide margin.
  • Drawing a glyph: the hot path. Move the cursor, set the colour if we have one, emit the glyph. Has to be cheap, because we’ll call it for every cell of every redraw.
  • Reading the keyboard: turn the platform’s idea of “a key was pressed” into a single byte we can switch on.
  • Timing and randomness: a millisecond clock for sleeps and a seed for the RNG, both pulled from whatever the host OS happens to expose.

Same five sections, two different implementations, and the game doesn’t care which one it links against. That’s the whole point.

Bundling the lot for the in-browser IDE

The in-browser IDE compiles a single C file. Both adapters cope with this the same way: retroc-dungeon.c is a tiny driver that #includes the platform implementation directly. There’s no makefile and no separate .o for the adapter, just one translation unit per platform preset, which is what the IDE wants. The native vbcc build on a real machine splits things back out into proper .c/.h files; the contract is the same either way.

Pick a platform

The two adapters diverge below this point, so the rest of this part of the series is split into two parallel tracks. Read whichever one matches the machine you care about, or read both, because the differences are where most of the fun is.

  • Atari ST: the VT52 + XBIOS adapter: why ESC E clears the screen in the wrong colour, what XBIOS Setcolor is for, and a long answer to “why can’t I just POKE the screen like on a C64?”.
  • Amiga: the AmigaDOS + ANSI adapter: three “rakes” I stepped on (dos.library, grey-on-grey, 300-baud feel), AmigaDOS Input() in raw mode, and ANSI escapes via fputs.

After both adapters are in place we’ll start treating them as one thing again and build the actual roguelike on top. See you in the next combined post.

The post Cross-Platform Amiga and Atari ST C Programming (“Not Conio But Compatible”) appeared first on Retro Game Coders.