Writing a Human68k Driver for the Nereid-X Ethernet Controller

The Sharp X68000 has always rewarded people willing to work below the application layer. Its Motorola 68000 processor, Human68k operating system, and expansion bus make it an excellent platform for learning how hardware, firmware, and software meet. Adding Ethernet through the Nereid-X board turns that exercise into something immediately useful: file transfers, remote administration, network boot experiments, and access to modern retrocomputing resources.

A network interface driver for Human68k must bridge several different worlds. The Nereid-X presents an Ethernet controller with its own register layout, buffers, and interrupt behavior. Human68k expects a device driver or resident service with a compatible calling convention. Applications, meanwhile, need a dependable packet interface rather than a collection of hardware-specific operations.

The most productive way to approach the project is to separate those concerns. First establish reliable register access and packet transmission. Then handle reception and interrupts. Only after that should the code be connected to a packet API, TCP/IP stack, or command-line utility. This layered method makes debugging much easier on original hardware.

Establishing The Hardware Contract

Before writing assembly, document the Nereid-X configuration as if the board had never been seen before. Record the controller model, I/O base address, interrupt line, Ethernet address storage, reset behavior, and any jumpers or configuration switches. If the implementation uses an NE2000-compatible controller, the programming model will resemble the 8390 family, but compatibility should be verified against the actual board design rather than assumed.

The driver should have one small hardware abstraction layer containing operations such as controller reset, register read, register write, remote-DMA transfer, transmit request, and interrupt acknowledgement. The rest of the code should call these routines instead of manipulating addresses directly. This keeps board-specific details in one place and allows a logic error to be isolated from a packet-processing error.

The X68000 bus and the Ethernet controller may disagree about access width and byte order. The 68000 is big-endian, while many Ethernet chips expose registers as byte-oriented devices and represent multi-byte values in a controller-specific order. A 16-bit access that appears efficient in source code can produce swapped addresses, incorrect length values, or corrupted packet data. Start with byte accesses unless the Nereid-X documentation explicitly authorizes a wider operation.

Hardware diagnostics also benefit from a stable display and input environment. When a test program changes the screen mode or produces an unstable picture during early bring-up, the CRT service-menu procedure is a useful reference for separating monitor geometry problems from driver failures.

Fitting Into Human68k

Human68k software can be built in several forms, and choosing the right form early prevents a great deal of rework. A standalone diagnostic program is ideal for initial register tests. A device driver loaded through CONFIG.SYS is more appropriate when the network interface should be available during system startup. A resident utility can expose packet services, but it must define its interface carefully and avoid interfering with DOS or IOCS calls.

A conventional driver entry point generally needs initialization, strategy or command dispatch, status reporting, and shutdown behavior. The exact shape depends on the intended API and the assembler or C toolchain. The important design principle is that initialization should validate the controller before claiming success. It should reset the chip, verify an expected register value, install the interrupt handler, configure the receive ring, and only then enable reception.

Do not perform DOS calls from an interrupt handler. The handler should save the necessary registers, identify the interrupt source, acknowledge it quickly, move or queue the packet, and return. File access, memory allocation, console output, and lengthy protocol processing belong in foreground code. Human68k systems are fast enough for useful networking, but they do not provide the isolation and scheduling guarantees of a modern operating system.

A clear internal state structure helps manage this division. It can contain the I/O base, IRQ information, MAC address, transmit status, receive-ring pointers, packet counters, error counters, and a flag indicating whether a packet is pending. Keeping mutable state together also makes it easier to inspect memory while the machine is stopped in a monitor.

Building A Minimal Packet Path

The first milestone should be a reset-and-identify utility rather than a complete network stack. Reset the controller, wait for it to become ready, read selected registers, and print the results. Then program a known station address and configure a receive mode that accepts directed packets. Promiscuous mode is useful for diagnosis but should not be the default because it increases traffic and makes buffer overruns more likely.

For transmission, construct a complete Ethernet frame in a known buffer. The destination address, source address, EtherType or length field, and payload must be present before the controller is instructed to send. Ethernet frames below the minimum payload size require padding, and the driver should account for this rather than expecting every caller to provide a correctly sized frame.

An NE2000-style controller commonly uses remote DMA to copy data between host memory and packet memory on the adapter. That operation has several failure points: the DMA address may be programmed in the wrong byte order, the byte count may be incorrect, or the command register may be changed before the transfer has completed. A conservative implementation waits for the controller’s completion indication after every transfer during early testing.

Reception usually uses a circular packet buffer managed by boundary and current-page pointers. The interrupt signals that one or more frames are ready, but the handler must walk the ring until it catches up. For each packet, validate the status, next-page pointer, and length before copying data to a software buffer. A malformed pointer should be treated as a driver error, not followed blindly into controller memory.

Area Initial implementation Later refinement Typical failure
Register access Byte-wide routines with delays where required Carefully tested wider transfers Swapped values or missed writes
Reset Poll a known status register Add timeout and recovery path Driver hangs during startup
Transmit One synchronous frame buffer Queue multiple frames Busy or lost transmit requests
Receive Interrupt plus ring polling Buffered packet queue Overrun or stale boundary pointer
Interrupts Acknowledge and count events Batch packet processing Repeated interrupt or lockup
API Simple send, receive, status calls Packet-driver or stack integration Ambiguous ownership of buffers
Diagnostics Console counters and loopback Persistent error logging Failure cannot be reproduced

A loopback test is especially valuable because it tests the transmit and receive paths without requiring another computer. If the controller supports internal loopback, use it first. External loopback through a hub or switch then verifies the physical transceiver, cable, and Ethernet negotiation. A packet capture on another machine provides an independent view of frame length, address fields, and timing.

Designing Interrupts And Buffers

Interrupt setup is often where a working register test becomes an unreliable driver. The Nereid-X interrupt line must be configured in a way that matches both the expansion hardware and the X68000 interrupt system. The handler should determine whether the Ethernet controller actually requested service before doing any packet work. Spurious interrupts must be acknowledged or ignored without disturbing an unrelated device.

Keep the handler short, deterministic, and reentrant-safe. It can copy a frame into a preallocated software buffer or record the controller’s receive position for later processing. A foreground polling routine can then validate headers, deliver packets to the caller, and update statistics. This reduces the amount of code running at interrupt level and avoids calling routines that assume normal program context.

Buffer ownership needs to be explicit. If the receive routine returns a pointer into a ring buffer, the caller must release it before the controller reuses that space. If the driver copies every packet into a queue, memory use increases but the API becomes easier to understand. For an early Human68k implementation, a small fixed queue is usually safer than dynamic allocation in an interrupt-sensitive path.

Transmit handling deserves the same discipline. A single buffer and a busy flag are sufficient for a diagnostic driver. Once a protocol stack is attached, packets may arrive while a previous frame is still being sent. A transmit queue, completion interrupt, and defined behavior for a full queue prevent accidental overwrites and make errors visible to the caller.

Connecting A Network API

The hardware layer should expose operations that are independent of TCP/IP: initialize, stop, get hardware address, send an Ethernet frame, receive an Ethernet frame, and retrieve statistics. A packet-driver-style interface is a practical choice because it allows multiple higher-level tools to share the device without embedding Nereid-X register knowledge in every program.

The API should define maximum frame size, minimum frame size, ownership of buffers, error codes, and whether calls block. It should also state how reception is signaled. A polling API is simple and useful for command-line utilities, while an interrupt-backed queue gives better performance for a resident TCP/IP stack.

Do not begin by implementing ARP, IP, ICMP, UDP, and TCP inside the hardware driver. That creates a large debugging surface and makes it difficult to decide whether a failed ping is caused by the Ethernet controller, frame construction, address resolution, or an IP checksum. First prove that raw frames leave and enter the machine. Then add a small ARP test, followed by ICMP echo, before attempting a complete network stack.

The driver should expose counters for transmitted frames, received frames, CRC errors, alignment errors, missed packets, transmit collisions, DMA failures, and resets. Human68k lacks the extensive observability common in current operating systems, so these counters are essential. A diagnostic command that prints them after each test can reveal whether the problem is electrical, timing-related, or purely logical.

Testing On Original Machines

Emulators are useful for checking calling conventions and basic Human68k behavior, but they cannot reproduce every expansion-bus timing issue. Test on a real X68000 with a known-good Nereid-X board, a stable power supply, and a simple network path. A small unmanaged switch is often preferable to a modern managed network that may introduce auto-negotiation or energy-saving behavior unfamiliar to older 10BASE-T hardware.

Use a staged test sequence. First verify reset and register identity. Next test internal loopback. Then transmit a fixed frame to a capture host. After that, receive a known frame addressed to the board. Finally, run bidirectional traffic for several minutes while watching error counters. Repeating each stage after a warm reboot and a cold power cycle can expose initialization assumptions.

Timing should be measured rather than guessed. Add bounded waits around reset, remote DMA, and transmit completion. Every wait needs a timeout and a recovery action, such as resetting the controller or returning a specific error. An infinite loop is especially damaging in a DOS-era environment because it can make the entire machine appear frozen.

Keep test frames deterministic. Use recognizable payload patterns, known lengths, and incrementing sequence numbers. A packet capture can then show whether bytes were reordered or dropped. When debugging a receive-ring problem, include the controller page numbers and software queue position in the diagnostic output. That information is often more useful than a generic “network error.”

Practical Priorities For The Driver

A maintainable Nereid-X driver should favor straightforward code over clever optimization. The X68000’s performance is sufficient for many retrocomputing tasks, while a difficult-to-audit fast path can make rare packet corruption almost impossible to diagnose. Document every magic register value, especially initialization bits whose meaning differs between compatible Ethernet controllers.

The following priorities provide a sensible implementation order:

  • Confirm the Nereid-X I/O address, interrupt configuration, controller identity, and station address before enabling network traffic.
  • Implement reset, byte-wide register access, internal loopback, and bounded timeout handling before writing a Human68k resident interface.
  • Keep interrupt code short and use fixed buffers with explicit ownership rules.
  • Add transmit and receive statistics from the first hardware test rather than retrofitting diagnostics later.
  • Publish the driver’s API, source code, build instructions, and known hardware limitations alongside the binary.

Source organization matters for preservation as much as runtime behavior. Separate assembly routines that touch the controller from portable packet and queue logic. Include a register map, timing notes, memory layout, and a test transcript in the repository. Future X68000 owners should be able to rebuild the driver with period-appropriate tools or port it to a newer assembler without reverse-engineering undocumented assumptions.

A useful release can begin as a diagnostic driver even if it does not yet support a full TCP/IP stack. Raw Ethernet transmission, packet capture compatibility, and reliable error reporting already provide value to hardware developers. Later versions can add an ARP utility, a lightweight UDP service, or integration with an existing Human68k networking package.

The Nereid-X is an excellent platform for preserving practical knowledge about the X68000 because it connects historical hardware to present-day networks. A carefully designed Human68k Ethernet driver demonstrates how the machine’s bus, interrupt system, operating system, and software ecosystem fit together. It also gives future enthusiasts a tested foundation instead of another isolated collection of register experiments.

Build the driver in small, observable stages, test each layer on real hardware, and publish the results with the source. That turns a personal networking project into a reusable part of the X68000 community’s technical record.

Nereid-X Expansion Board

A personally-produced LAN+USB+Memory expansion board for Sharp X68000 series computers. Multiple production runs were offered, including a final batch and a later revival reproduction run.

Power Supply Repair

X68 power supply repair and modification services were offered by the site owner, with documentation shared through diary entries spanning 2001–2006.

Server & Networking

Notes on FreeBSD administration, ISP changes, server migration, and networking topics. The site itself ran on FreeBSD with the hns diary system and Namazu search integration.

A two-ink risograph print in muted slate-blue and charcoal on off-white paper, showing a stylized desktop computer monitor beside a circuit board with soft geometric trace lines, conveying a calm retro-computing workshop atmosphere. A two-ink risograph print in deep purple and dark grey on cream stock, depicting a compact expansion card with connector ports and subtle Japanese technical annotations, evoking a hobbyist electronics bench. A two-ink risograph print in teal and charcoal on warm white paper, showing a server rack silhouette with soft network-line motifs and a small weather icon, suggesting a personal server room corner.

Get in touch

X68K.NET connects Sharp X68000 enthusiasts through community links and shared projects. Reach out with questions about the Nereid project or X68 resources.