Even before the standardisation, methods of doing variable arguments were common. The C89 standard gave a ... standardized interface based on the existing practices. Remainders of those practice still exist at place, for instance <varargs.h>
was still present in Unix98: http://www.opengroup.org/onlinepubs/007908799/xsh/varargs.h.html (but is no more present in current version)
The implementation of the macros was always very much system dependent (there are stacks growing in both directions, there are even systems using a linked list as stack, position of various things in the stack frame depend on the processor and common convention, on some processors -- says Sparc -- one need first to save the registers, alignment requirements may cause problems,...)
If you want to know what would a simple implementation looks like, here is one, depending on assumptions probably false (they don't try to get any alignment right) and also certainly failing some corner cases even when the assumptions hold:
typedef void* va_list;
#define va_start(va, arg) va = (void*)((&arg)+1)
#define va_arg(va, type) (va = (void*)(((type*)va) + 1), *((type*)va -1)
#define va_end(va)