Getting Pixel-Perfect Output from Pascal Isn't Actually That Hard Once You Figure Out the Bit Level
I spent too many hours wrestling with VGA mode 13h and wondering why my circles looked like octagons before I stopped fighting the hardware and started working with it. The core approach involves setting your graphics mode, calculating pixel coordinates, and writing directly to video memory. Everything else is optimization and fixing the mistakes you made along the way. The standard path goes through Borland Pascal or Free Pascal with the graph unit. You start by calling InitGraph and specifying a VGA driver. Mode 13h gives you 320 by 200 with 256 colors simultaneously, which was the sweet spot for PC graphics in the early nineties. If you need higher resolution, you move into Mode X techniques or VESA mode 0x101, which gives you 640 by 480 at 16 or 256 colors. The tradeoff is that Mode X requires banking through the 0xA000 segment, which adds a layer of complexity most tutorials skip over entirely. Video memory lives at segment 0xA000. Each pixel in mode 13h is a single byte containing a palette index. Drawing a line means iterating across coordinates and writing those bytes directly. The compiler handles the segment register manipulation if you declare pointers properly, but doing it manually with inline assembly or absolute variables is noticeably faster.
The Actual Drawing Loop
Here's how I typically structure the main render loop. You set up your variables, open the graphics mode, clear the screen by writing zero bytes across the entire 64K video memory block, then run your rendering routines. For something like a fractal or a terrain mesh, you iterate through each scanline and calculate the pixel values on the fly. The bottleneck is always the memory write speed, not the calculation itself. I wrote a ray tracer in Pascal back when I was trying to learn 3D rendering, and the first version took about forty minutes to render a single 320 by 200 frame. The problem wasn't the algorithm. It was that I was calling GetPixel and PutPixel from the graph unit for every single point, which added massive overhead through function calls and mode switching. Switching to direct memory access via a pointer reduced that to roughly twelve seconds per frame. That's the kind of optimization that makes or breaks real-time graphics in Pascal.
Direct Memory Access vs. Graph Unit Functions
The graph unit functions like PutPixel are convenient. They're also painfully slow for anything beyond simple line drawings. When you call PutPixel, it validates coordinates, checks clipping bounds, converts logical coordinates if needed, and then writes to memory. Each of those steps adds microseconds. Multiply that by 64,000 pixels per frame and you're looking at significant lag. The workaround is straightforward. Declare a pointer to the video segment and write directly. In Free Pascal, this looks like defining a pointer to segment A000h and casting it to a byte array. In older Borland Pascal, you use absolute variables pointing to the video memory address. The syntax varies slightly between compilers, but the principle is identical. You're bypassing the abstraction layer entirely. I've seen experienced developers dismiss this as premature optimization, but in practice it's the difference between a demo that runs and one that doesn't. The direct access approach typically cuts rendering time by about eighty percent on complex scenes.
Get the Full Details

Common Pitfalls Nobody Talks About
The first thing that catches people out is palette management. Mode 13h uses a 256-color palette, and the default palette is garbage. You need to load your own palette using the port $3C8 and $3C9 registers. Each color is three bytes: red, green, and blue, each ranging from 0 to 63. If you skip this step, your rendered image will look washed out and wrong. Setting up a proper palette takes about thirty lines of code and fifteen minutes of your time. The second issue is page flipping. If you're drawing frame by frame to mode 13h, you'll see tearing. The solution is double buffering in software. You draw to a memory buffer in RAM, then copy the entire buffer to video memory in one operation. This usually takes about six milliseconds on a Pentium-era machine. The copy operation is fast enough that you won't notice the delay during normal rendering. The alternative is enabling the CRTC register to switch display pages, but that requires more hardware-specific knowledge and doesn't work reliably on all systems. I hit a really annoying edge case once where my high-resolution Mode X rendering would freeze the system after about twenty minutes of continuous use. The culprit turned out to be an unhandled IRQ from the sound card. My audio routines were sharing the same interrupt vector, and the graphics banking code was interfering with the DMA controller. The fix was to disable the sound card IRQ during rendering and re-enable it after the frame was complete. It added about two milliseconds per frame but eliminated the crashes entirely. This kind of hardware interaction issue is nearly impossible to find through debugging alone because it's timing-dependent.
Scaling to Higher Resolutions
If you need something beyond 320 by 200, VESA 0x101 at 640 by 480 is the next logical step. You access it the same way through the A000 segment, but you have to bank in different memory pages using port writes to 0x03C4 and 0x03C5. Each page is 64K, so a full 640 by 480 frame spans multiple pages. The rendering code needs to switch pages between scanline batches, which adds overhead but is manageable. For true high resolution, you can go to 32-bit color modes using VESA 0x105 or similar. This requires packing pixel values as four-byte integers instead of single bytes. The calculation changes slightly because you're now writing word-sized or dword-sized values instead of bytes. Memory bandwidth becomes the limiting factor here. On older hardware, you might render at half the frame rate compared to 256-color mode even though the color depth is dramatically better. This is worth considering if your target system is anything older than a Pentium II.
What This Approach Can't Do Well
Pascal graphics through these methods are fundamentally limited by the hardware they run on. There's no hardware acceleration, no texture mapping pipeline, and no shader support. If you're trying to build something that resembles modern game graphics, you'll hit a wall very quickly. The rendering is purely software-based, which means complexity scales linearly with polygon count. A scene with a thousand triangles will look fine at 60 frames per second on a modern CPU. The same scene on a machine from the late nineties will struggle to maintain 10 frames per second. This isn't a Pascal problem. It's a fundamental limitation of software rendering. Another honest limitation is that modern operating systems don't play nicely with direct video memory access. Windows NT-based systems, Linux with modern X servers, and anything running in a virtual machine will block or ignore your attempts to write to 0xA000. This means your Pascal graphics program will only run reliably on DOS, FreeDOS, or in a DOS emulator like DOSBox. If you need cross-platform compatibility or modern OS support, you should look into SDL or a similar library instead. They abstract away the hardware access and handle the platform differences for you, though you lose the direct performance control that comes with bare metal programming. The source code for most of these techniques is available through classic Pascal archives and retro computing communities. Free Pascal comes with example graphics programs in the samples directory that cover mode 13h setup, basic shapes, and palette manipulation. Extending those examples into whatever you're trying to build is usually a matter of copying the initialization code and replacing the demo rendering with your own logic. The initialization code is the part that tends to break if you modify it incorrectly, so leave it alone until you understand exactly what each line does.
