Lo estás descifrando bien
GETENV te devuelve valores asociados al entorno, como la version del compilador, pero la mayoria referidos al micro usado.
getenv("SFR:PORTB") devuelve la dirección del PORTB, escribir:
#BIT Out1 = getenv("SFR:PORTB").0 ; es lo mismo que #BIT Out1 = 0x6.0 ;
La utilidad es que si cambiás de micro te puede cambiar la dirección del puerto, de esta manera te olvidás donde está.
El macro T_OUT(1) expandido resulta: (le agregué comentarios, no forman parte de la expansión)
/* Expansion de T_OUT(1)*/
if(npuls1 !=0) // npuls1 es el número de pulsos pendientes de enviar.
if(--n1 ==0){ // Cuando n1 llegue a 0 hay que cambiar de estado la salida
n1 = W1 ; // recarga n1
if(Out1==1) Out1 = 0 ;
else if(--npuls1 !=0) Out1 = 1 ; // Si Out1 estaba en 0 entonces toca mandar el siguiente, salvo que haya sido el úlltimo.
// Esto ya no vuelve a arrancar hasta que se asigne a npuls1 un valor mayor que 0. De eso se encarga secuencia()
}
El n##x significa que a la 'n' le agregas el argumento 'x' . Si x es 1 te forma 'n1'
De esa manera genero en la expansion los nombres de las variables 'n1', 'n2', 'n3','n4' ,Out1,Out2... etc.
Se puede usar mas de un argumento, por ejemplo hacer:
#define T_OUT(x,w) \
static int8 n##x = w ; \
if(npuls##x !=0) \
if(--n##x ==0){ \
n##x = w ; \
if(Out##x ==1) Out##x = 0 ; \
else if(--npuls##x !=0) Out##x = 1 ; \
}
Entonces invoco el macro con el número del contador y el ancho del pulso.
T_OUT(1,19) se expande en:
static int8 n1 = 19 ;
if(npuls1!=0)
if(--n1==0){
n1 = 19 ;
if(Out1==1) Out1 = 0 ;
else if(--npuls1!=0) Out1 = 1 ;
}
Los parámetros y el estado de los contadores podrían estar dentro de una array y no andar repitiendo el código. La elección dependerá de la memoria de programa y RAM disponible y el tiempo admisible dentro de la interrupción.