A controller can be thought of as operating internally within its own local coordinate system. For both keyframe and procedural controllers, the data generated is provided in the local space of the controller. It is not until the controller supplies its transformation to 3ds Max does the coordinate system become relative to anything other than the controller itself.
Every controller, regardless of type, provides data to 3ds Max at a specified time via Control::GetValue(). One of the parameters passed to this method is a void pointer. Depending on the controller type, different data types are passed using this pointer (see Control::GetValue() for a complete discussion of all the data types). For example, a transform controller will be passed a Matrix3 data type. It is the job of this transform controller to update the matrix with its transformation. It does this by pre-multiplying its transformation with the matrix passed.
The controller operates within its own space internally, but when it pre-multiplies its data with the matrix passed, it essentially means the controller is then working in the coordinate system of the matrix. Another way to think of this is that the Matrix3 passed is actually transforming the controllers data. Thus, what the final coordinate system ends up being depends upon the client of the controller and not on the controller itself -- it depends on the matrix passed to GetValue().
To better understand this consider the following examples. For a transform controller, the matrix passed to GetValue() is the transformation matrix of the node's parent. Consider a simplified keyframe transform controller that only provided position (no rotation or scale). This controller would just update the bottom row of the matrix (translation data) passed by pre-multiplying its value. If the node the controller was assigned to was not hierarchically linked, that is it had no parent other than the world, then the matrix passed to GetValue() would be the identity. In this case, when the controller pre-multiplied its value, it would simply be setting the translation row.
Consider a different case when the node the controller is assigned to is hierarchically linked. As before, the keys of the controller are stored in the local space of the controller. But in this case, when GetValue() is called, the matrix passed is not necessarily the identity. Rather it will be the transformation matrix of the parent of the node the controller is assigned to. If the parent is not located at the origin, or is scaled or rotated in some way, the parent matrix passed will provide an additional transformation to the one the controller itself provides.
For transform controllers, the matrix that is updated in GetValue() becomes the node transform matrix and is used to position and orient the node in the scene.