Reading List

The Selfish Gene
The Psychopath Test: A Journey Through the Madness Industry
Bad Science
The Feynman Lectures on Physics
The Theory of Everything: The Origin and Fate of the Universe


ifknot's favorite books »

Sunday, 19 April 2026

DOSBox_joker.png CGA mode 6 "hi res"

Getting Zed and Clangd to Be Useful for the Retro Programmer

My editor of choice is Zed. It is fast, modern and minimal. My compiler is Open Watcom (which is a modernised Watcom) ancient and idiosyncratic but correct for the IBM XT target I am writing for. The problem is that clangd, the language server that Zed uses for C and C++ code, has opinions about my code - strong opinions.

As I have argued elsewhere, modern language servers and retro compilers are not natural bedfellows. Clangd expects C17, not 8086 assembly with __fastcall calling conventions and inline __asm blocks. Open Watcom accepts all of that happily, because it was built for the retro target, but clangd was not. The two do not speak the same dialect, and the result, without configuration, is an editor full of red squiggly underlines under perfectly valid code.

Consider then, a typical function from my codebase:

void __fastcall cga_hi_res_plot(cga_coord_t x, cga_coord_t y, cga_colour_t colour) {
   __asm {
      .8086
       mov     ax, x
       
       etc...
  }
}

Clangd flags __fastcall as an unsupported calling convention. It flags __asm as unknown syntax. It has no idea what .8086 means. The code compiles without complaint under Watcom and the XT runs it beautifully, but my editor thinks I am writing nonsense.

The Solution a .clangd File

The most reduced form of a working configuration for this problem is a .clangd yaml file in the project root directory that Zed will honour:

CompileFlags:
Add: [-xc, -Wno-ignored-attributes, -fms-extensions]
Remove: [-std=c99]

The -xc flag forces clangd to treat the code as C rather than Objective-C++, which for reasons I do not fully understand is sometimes the default. The -Wno-ignored-attributes flag suppresses the warning about __fastcall being unsupported, which is a lie but a lie we can live with. The -fms-extensions flag embraces the Microsoft and Watcom dialect, which helps with the rest of the weirdness. Removing -std=c99 prevents clangd from insisting on a language standard that my code does not fully follow because neither does Watcom.

The Include That Was Not Unused

A second problem emerged after the squiggles were gone. I have a lookup table for CGA row offsets:

#include "cga_lookup_table_y.h"   // clangd says: unused and paints it with yellow squiggles

The header is used extensively, but it is used inside __asm blocks. Clangd cannot see past the veil of inline assembly, so it flags the include as dead code. This is a false positive, but it is a persistent one.

The solution is a pragma from the Include-What-You-Use (IWYU) tooling:

#include "cga_lookup_table_y.h"   // IWYU pragma: keep

This communicates to the analyser that the inclusion is intentional, even if the static analysis cannot observe its usage. The warning disappears, the header remains, and the code continues to work.

Conclusion

So, fellow retro programmers, you can have best of both worlds. With Zed and .clangd configuration you can transform a hostile language server into a useful assistant. The defining properties of such a configuration are that it accepts non-standard calling conventions with -Wno-ignored-attributes, accepts the Watcom dialect with -fms-extensions, preserves includes hidden behind assembly with // IWYU pragma: keep, and - most importantly - lets you keep code completion.

No comments:

Post a Comment