This guide walks you through building and deploying your first CNet 5 PFile (door) using the modernized SDK and the m68k-amigaos-gcc cross-compiler.
- m68k-amigaos-gcc cross-compiler installed at
/opt/amiga/(or adjust paths in the Makefile). This is the Bebbo GCC toolchain for AmigaOS 68k. - A running CNet 5 BBS (v5.x) on a real or emulated Amiga, accessible for file transfer (e.g., via network share, FTP, or amigactl).
- Basic familiarity with C programming and the AmigaOS environment.
Clone the repository and verify the cross-compiler is available:
git clone <repo-url> cnet-sdk
cd cnet-sdk
/opt/amiga/bin/m68k-amigaos-gcc --versionBuild the support library (libcnet_door.a) that all door programs link against:
make libThis compiles the door lifecycle code, CNC_* wrapper functions, and utility routines into lib/libcnet_door.a.
Create a file called hello.c:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <exec/types.h>
#include <cnet/cnet.h>
extern struct PortData *z;
extern struct MainPort *myp;
extern BOOL CNCL_DoorInit(int argc, char **argv);
extern void CNCL_DoorCleanup(void);
/* Wrapper function prototypes (from libcnet_door.a) */
extern void CPutText(char *text);
extern int CEnterLine(UBYTE len, ULONG flags, char *prompt);
extern void CPutA(void);
static void DoorCleanup(void)
{
/* Free any resources your code allocated */
}
int main(int argc, char **argv)
{
atexit(DoorCleanup);
if (!CNCL_DoorInit(argc, argv)) {
printf("This is a CNet door -- run from CNet BBS.\n");
return 0;
}
CPutText("\r\nHello from my first PFile!\r\n\r\n");
if (CEnterLine(40, 0, "What is your name? ")) {
sprintf(z->ABuffer, "Nice to meet you, %s!\r\n", z->InBuffer);
CPutA();
}
return 0;
}CNCL_DoorInit(argc, argv)-- Opens cnetc.library, establishes the door context, and populates the global pointersz(PortData) andmyp(MainPort). CNet passes the communication port name asargv[1].CPutText()-- Sends text to the connected user. Use\r\nfor line breaks.CEnterLine()-- Prompts the user for a line of text. The result goes intoz->InBuffer.CPutA()-- Sends the contents ofz->ABufferto the user. Format your output intoz->ABufferwithsprintf(), then callCPutA()to display it.atexit(DoorCleanup)-- Registers your cleanup beforeCNCL_DoorInitregisters its own, so yours runs first (LIFO order). Whenmain()returns orexit()is called,DoorCleanupruns first, thenCNCL_DoorCleanupshuts down the CNet connection.
CNet's struct headers (included via <cnet/cnet.h>) redefine __asm to nothing for SAS/C compatibility. This conflicts with GCC's __asm keyword, which the AmigaOS NDK proto headers use for register binding in inline library stubs.
<cnet/cnet.h> handles the include ordering: it pulls in all necessary system headers (exec, dos, graphics, intuition) before redefining __asm, so the NDK inline stubs are compiled correctly. However, this means you cannot call CNC_* functions (which use <proto/cnetc.h> inline stubs) directly in the same file that includes <cnet/cnet.h>.
The SDK solves this with wrapper functions in libcnet_door.a:
| Wrapper (use in your code) | Underlying library call |
|---|---|
CPutText() |
CNC_PutText() |
CEnterLine() |
CNC_EnterLine() |
COneKey() |
CNC_OneKey() |
CPutA() |
CNC_PutA() |
CPutQ() |
CNC_PutQ() |
CWaitForInput() |
CNC_WaitForInput() |
CReadFile() |
CNC_ReadFile() |
CReadGraphics() |
CNC_ReadGraphics() |
CSetDoing() |
CNC_SetDoing() |
CCallEditor() |
CNC_CallEditor() |
CShutDown() |
CNC_ShutDown() |
CCheckCarrier() |
CNC_CheckCarrier() |
| ... | ... |
Use the C* wrapper names in your door code. See templates/pfile_cnetc.c for the full list.
Alternative approach for standalone utilities: If you need to call Exec/DOS library functions AND access CNet structs in the same file without the door framework, include the system proto headers first, then define only the SAS/C compat macros you need (excluding __asm), then include CNet struct headers individually. See templates/standalone.c for this pattern.
m68k-amigaos-gcc -noixemul -m68020 -O2 -Wall -Wno-pointer-sign \
-Iinclude -Iinclude/cnet -o hello hello.c -Llib -lcnet_doorFlags explained:
| Flag | Purpose |
|---|---|
-noixemul |
Use libnix (lightweight AmigaOS C runtime), not ixemul.library |
-m68020 |
Generate 68020+ code (CNet 5 requires 68020 or better) |
-O2 |
Optimization level |
-Wall -Wno-pointer-sign |
Warnings on, but suppress char/UBYTE pointer sign mismatches common in CNet headers |
-Iinclude -Iinclude/cnet |
SDK header search paths |
-Llib -lcnet_door |
Link against lib/libcnet_door.a |
The output is a native AmigaOS 68k executable.
For anything beyond a quick test, add your program to the top-level Makefile. Door programs go in the CNETC_DOORS list:
CNETC_DOORS = \
examples/empty \
examples/CNetCTest \
hello \
...Then make examples builds everything.
Copy the compiled binary to the Amiga's PFiles: directory (or any path CNet can reach). Common methods:
- Network share: Copy to a Samba/NFS share mounted on the Amiga.
- amigactl:
amigactl put hello PFiles:hello - FTP: Upload to the Amiga's FTP server.
The binary must be accessible from the Amiga side as a path like PFiles:hello or cnet:doors/hello.
- From the CNet sysop console or a sysop-level telnet session, enter BBSMENU (the subboard/menu editor).
- Navigate to the subboard list where you want the door to appear.
- Add a new subboard entry:
- Type: PFile/C (marker type 5 /
MRK_CNETC_DOOR) - Title: The name users will see (e.g., "Hello Door")
- Path: The AmigaDOS path to your binary (e.g.,
PFiles:hello) - GO keyword: Optional shortcut (e.g.,
HELLO) so users can typeGO HELLO
- Type: PFile/C (marker type 5 /
- Save the entry.
The subboard tree organizes all areas -- message bases, file areas, doors, text files -- into a hierarchical structure. Your door appears as a leaf node that users reach by navigating the menu or using the GO keyword.
- Connect to the BBS via telnet (or local console).
- Navigate to the subboard where you configured the door, or type
GO HELLO. - CNet launches your binary, passing the CPort name as
argv[1]. - You should see your greeting text and input prompt.
- When you enter text and press Return, the door displays the response and exits.
- CNet returns the user to the BBS menu.
- If the door fails to launch, check that the binary path in the subboard config is correct and the file has execute permissions.
- If you see "This is a CNet door -- run from CNet BBS", the binary was run from a CLI shell instead of being launched by CNet (no
argv[1]CPort name). - CNet's
cnetc.library(v4+) must be present inLIBS:on the Amiga. - The default stack size set by
libcnet_door.ais 64KB (__stack = 65536). If your door uses large buffers, increase it by definingunsigned long __stack = 131072;(or higher) in your source file.
- Study
templates/pfile_cnetc.cfor the complete door template with all wrapper prototypes. - Study
templates/standalone.cfor writing CLI utilities that read CNet state without the door framework. - Read
docs/architecture.mdto understand the CNet process model, MainPort, and PortData. - Browse
examples/for working programs demonstrating various SDK features.