Radeon R500


PIC

Contents

1 Introduction
1.1 Hardware
1.2 Test setup
2 Progress: 07 Oct 2025
2.1 Documentation
2.2 AtomBIOS
2.3 PIO mode
2.4 Triangle drawing attempt #1

1 Introduction

The primary/minimal project goal is ”draw a triangle on a Radeon R500 via direct memory-mapped hardware register and texture memory accesses”. This means no Mesa, no radeon kernel module, and certainly no OpenGL or Direct3D.

I have worked directly with several other graphics units in the past ( Saturn VDP1, Dreamcast Holly, Voodoo 2). In all of these projects, my strategy is generally:

The rabbit hole for R500 seems significantly deeper, considering this is the first graphics unit I’ve worked with that has programmable vertex and pixel shader engines.

1.1 Hardware

For testing, I currently have this hardware configuration:

I also have the X1950 XT PCIe shown in the photo, which amazingly has never been used, and prior to the photo was sealed in an antistatic bag from manufacture to now.

1.2 Test setup

While in my other (video game console) projects I typically insist on “bare-metal” development with no operating system or third-party library running on the target hardware, my experience with x86 is much more limited.

While it is something I am interested in doing, I believe creating a zero-dependency “code upload” mechanism for an x86-pc that does not depend on an operating system would severely delay my progress on R500-specific work.

For my initial exploration of R500, I will instead be manipulating the hardware primarily from Linux kernel space. This Linux kernel code does not actually meaningfully depend on Linux APIs beyond calling ioremap to get usable memory mappings for R500 PCI resources (texture/framebuffer memory and registers).

2 Progress: 07 Oct 2025

From 01 Oct 2025 to 07 Oct 2025, I achieved the following:

I did not achieve the following:

2.1 Documentation

In general, I note that the R500 documentation is significantly weaker than I hoped, and does not contain enough information to draw a triangle on the R500 from the documentation alone (with no prior knowledge about previous Radeon graphics units).

In addition to the lack of prose, in several cases I’ve noticed both Mesa and Linux reference R500 registers that are not present at all in the documentation.

2.2 AtomBIOS

AtomBIOS physically exists as a section inside the ROM on R500 graphics units. AtomBIOS is notably used for setting PLL/pixel clock frequencies and display resolutions, among several other functions.

The Radeon graphics hardware itself does not execute AtomBIOS code–instead, it is expected that the host (e.g: x86) CPU evaluate the instructions in the AtomBIOS command tables. Generally the outcome of evaluating AtomBIOS code is that several “register write” instructions will be executed, changing the state of the graphics unit.

My original goal in studying AtomBIOS was that I thought I would need it to set up the R500 display controller to a reasonable state (as a prerequisite for drawing 3D graphics). However, after actually experimenting with “disable VGA mode”, I currently believe that I don’t actually need to implement resolution/mode changes, and can proceed without it.

2.3 PIO mode

The Linux kernel exclusively communicates with R500 via “PCI bus mastering”. A “ring buffer” is allocated in “GTT” space, which from the graphics unit’s perspective exists in the same address space as framebuffer memory, but is an address that is outside the framebuffer memory that physically exists.

I also observed via debugfs that the GTT apparently involves some sort of sparse page mapping, but I don’t understand how this works from an x86 perspective.

In the absence of an understanding of how to make my own “GTT” address space, I attempted to operate the R500 in “PIO” mode. This has the advantage of being able to simply write to registers via (simple) PCI memory-mapped accesses, but it has the disadvantage that Linux doesn’t use R500 this way, so I have no reference implementation for how PIO mode should be used.

2.4 Triangle drawing attempt #1

I translated my glDrawArrays notes to equivalent register writes.

This does not work, and I don’t yet understand why. The main issue is that most of the time when I execute that code, Linux appears to “hang” completely, and my “printk” messages are never sent over ssh. On the rare occasion when the “hang” does not occur, a triangle is nevertheless not drawn on the framebuffer.

I have a few ideas for how to proceed:

The latter is perhaps both the most attractive, and the most work. I currently don’t have any understanding of GEM buffers, radeon buffer objects, etc.., so I’d need to study these in more detail.