In top-level (not macro-replaced) input, macro invocations argument lists are parsed immediately, before the arguments are macro expanded. (This must be the case, because macro arguments used with # and ## macro operators are not expanded, and that's not known until the replacement body is examined.)
However, parameters in the replacement body are replaced with the respective arguments (which will have been macro-expanded if the parameter is not used with # or ##) before the replacement body is scanned for macros.
So you can achieve the effect you want by adding a level of indirection. However, you probably want to use a varargs macro, in case the invoker puts in an explicit comma:
#define CHECK(...) CHECK_(__VA_ARGS__)
#define CHECK_(reg, n) ((reg >> n) & 1U)
#define TEST_ARGS 10, 1
CHECK(TEST_ARGS)
Test:
$ $ gcc -x c -E - <<<'#define CHECK(...) CHECK_(__VA_ARGS__)
> #define CHECK_(reg, n) ((reg >> n) & 1U)
>
> #define TEST_ARGS 10, 1
> CHECK(TEST_ARGS)'
# 1 "<stdin>"
# 1 "<built-in>"
# 1 "<command-line>"
# 31 "<command-line>"
# 1 "/usr/include/stdc-predef.h" 1 3 4
# 32 "<command-line>" 2
# 1 "<stdin>"
((10 >> 1) & 1U)