Its much more fun when you can get away from the preprocessor-
#include <stdint.h>
typedef enum {
A0=0x400, A1, A2, A3, A4, A5, A6, A7,
B0=0x420, B1, B2, B3, B4, B5, B6, B7,
C0=0x440, C1, C2, C3, C4, C5, C6, C7,
D0=0x460, D1, D2, D3, D4, D5, D6, D7,
E0=0x480, E1, E2, E3, E4, E5, E6, E7,
F0=0x4F0, F1, F2, F3, F4, F5, F6, F7,
} PIN_t;
typedef enum { OUT=1, IN } DIR_t;
typedef enum { PULLUP_OFF, PULLUP_ON } PULLUP_t;
static inline void pin_dir(PIN_t pin, DIR_t io){
((volatile uint8_t*)(pin & 0XFF0))[io] = 1<<(pin&7);
}
static inline void pin_pullup(PIN_t pin, PULLUP_t pu){
//with pullup wanted, also assume is an input
if(pu){
pin_dir(pin, IN);
((volatile uint8_t*)(pin & 0XFF0))[0x10+(pin&7)] |= 1<<3;
} else {
((volatile uint8_t*)(pin & 0XFF0))[0x10+(pin&7)] &= ~(1<<3);
}
}
static inline void pin_set(PIN_t pin, uint8_t hl){
if(hl)((volatile uint8_t*)(pin & 0XFF0))[5] = 1<<(pin&7);
else ((volatile uint8_t*)(pin & 0XFF0))[6] = 1<<(pin&7);
}
static inline void pin_toggle(PIN_t pin){
((volatile uint8_t*)(pin & 0XFF0))[7] = 1<<(pin&7);
}
static inline uint8_t pin_get(PIN_t pin){
return ((volatile uint8_t*)(pin & 0XFF0))[8] & (1<<(pin&7));
}
#define sw1 A2
#define led1 B3
int main(){
pin_pullup(sw1, PULLUP_ON);
pin_dir(led1, OUT);
//led1 lights when sw1 pressed
for(;;){
pin_set(led1, !pin_get(sw1));
}
}
/*
Disassembly of section .text:
00000000 <main>:
//sw1 input
0: 84 e0 ldi r24, 0x04 ; 4
2: 80 93 02 04 sts 0x0402, r24 ; 0x800402 <_edata+0x3a2>
//sw1 pullup on
6: 80 91 12 04 lds r24, 0x0412 ; 0x800412 <_edata+0x3b2>
a: 88 60 ori r24, 0x08 ; 8
c: 80 93 12 04 sts 0x0412, r24 ; 0x800412 <_edata+0x3b2>
//led1 output
10: 88 e0 ldi r24, 0x08 ; 8
12: 80 93 21 04 sts 0x0421, r24 ; 0x800421 <_edata+0x3c1>
//get sw1 (loop starts here)
16: 90 91 08 04 lds r25, 0x0408 ; 0x800408 <_edata+0x3a8>
1a: 92 fd sbrc r25, 2
1c: 03 c0 rjmp .+6 ; 0x24 <__zero_reg__+0x23>
//sw1=0, so set led1=1
1e: 80 93 25 04 sts 0x0425, r24 ; 0x800425 <_edata+0x3c5>
22: f9 cf rjmp .-14 ; 0x16 <__zero_reg__+0x15>
//else sw1=1, so led1=0
24: 80 93 26 04 sts 0x0426, r24 ; 0x800426 <_edata+0x3c6>
28: f6 cf rjmp .-20 ; 0x16 <__zero_reg__+0x15>
*/
I'm no expert, but the more you let the compiler handle things, the better.
The above may not be perfect, bu the only 2 defines above are to give friendly names for the pins. I don't need a thousand lines of defines, and each function- whether inline or not can be easily tested/tweaked.
As can be seen above, the compiler does a pretty good job (I used -Os).