Ja PicNick hat recht. Es wird einfach ein TWI Interrupt gestartet. Dort kann man das TWBR Register abfragen.
Theoretisch hab ich sowas mal gemacht mit WinAVR. Vielleicht hilfts dir.
Es gibt 5 read only Register und 5 write only Register.
Code:
#include <avr/io.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
#include <avr/twi.h>

#define i2c_slave_init TWCR= _BV(TWEA) | _BV(TWEN) | _BV(TWIE) 
// Slave init und I2C Interrupt enable

#define slave_addr_init TWAR |= _BV(TWA0)
// Slave addresse auf 0x10 also auf 10, keine reaktion auf globale anfragen

// Die read_only Register werden im Hauptprogramm regelmäßig mit z.B. Sensorwerten aktuallisiert
// Die write_only Register werden regelmäßig im Hauptprogramm ausgelesen um auf deren Werte zu reagieren
volatile unsigned char read_register1,read_register2,read_register3,read_register4,read_register5;
volatile unsigned char write_register1,write_register2,write_register3,write_register4;
volatile unsigned char regwrite_flag=0; 
volatile unsigned char regvalue=0;

SIGNAL(SIG_2WIRE_SERIAL)
{
   if(TWSR==TW_SR_SLA_ACK|TWSR==TW_SR_ARB_LOST_SLA_ACK|TWSR==TW_SR_DATA_ACK)   // Slave Reciever Datenpaket empfangen
   {
      if(regwrite_flag==1)
	  {
	     if(regvalue==6){                
		   write_register1=TWDR;
		   regwrite_flag=0;
		   regvalue=0;}
	     if(regvalue==7){                
		   write_register1=TWDR;
		   regwrite_flag=0;
		   regvalue=0;}
		 if(regvalue==8){                
		   write_register1=TWDR;
		   regwrite_flag=0;
		   regvalue=0;}
		 if(regvalue==9){                
		   write_register1=TWDR;
		   regwrite_flag=0;
		   regvalue=0;}
		 if(regvalue==10){                
		   write_register1=TWDR;
		   regwrite_flag=0;
		   regvalue=0;}
	  }
	  else
	  {
	  if(TWDR==1){                          // Register 1 until 5 only read; 
	     TWDR=read_register1;
		 TWCR|=(1<<TWINT);}
	  else if(TWDR==2){
	     TWDR=read_register2;
		 TWCR|=(1<<TWINT);}
	  else if(TWDR==3){
	     TWDR=read_register3;
		 TWCR|=(1<<TWINT);}
	  else if(TWDR==4){
	     TWDR=read_register4;
		 TWCR|=(1<<TWINT);}
	  else if(TWDR==5){
	     TWDR=read_register5;
		 TWCR|=(1<<TWINT);}
	  else if(TWDR==6||TWDR==7||TWDR==8||TWDR==9||TWDR==10){ 	  // Register 6-10 write only register.
         regwrite_flag=1;   // Flag wird gesetzt damit beim nächsten mal der TWDR Wert als Registerinhalt
		 regvalue=TWDR;     // und nicht als Registernummer interpretiert wird.
		 TWCR|=(1<<TWINT);}
	  
		 
	  // Hier könnte man noch weiter Befehle eingeben die dann mit der Entsprechenden TWDR ausgeführt werden
      }
   }	  
   else if(TWSR==TW_SR_DATA_NACK)
      TWCR = (1<<TWINT) | (1<<TWEA);
   else if(TWSR==TW_ST_SLA_ACK|TWSR==TW_ST_ARB_LOST_SLA_ACK|TWSR==TW_ST_DATA_ACK) // Dann ist Slave Transmitter mode und es wird einfach das byte in TWDR
      TWCR |= (1<<TWINT);                     // Versand
   else if(TWSR==TW_ST_DATA_NACK|TWSR==TW_ST_LAST_DATA)
      TWCR = (1<<TWINT) | (1<<TWEA);
   else if(TWSR==TW_SR_STOP)              // STOP condition has been recevied
      TWCR=(1<<TWINT) | (1<<TWEA);
TWCR |= 0b10000000;
}


void main(void)
{
   i2c_slave_init;
   slave_addr_init;
   for(;;)
   {
      /* read_register1=   ;
	     read_register2=   ;
	     read_register3=   ;
	     read_register4=   ;
	     read_register5=   ;
	   
         if(write_register1==  )
		 
	     if(write_register2==  )

	     if(write_register3==  )

	     if(write_register4==  )

	     if(write_register5==  )

      */
   }
}
Ich hab auch mal was für SPI geschrieben. Dabei können beide Master und Slave werden. Allerdings auch nur theoretisch, also ich hatte noch keine Möglichkeit es zu testen. Hab leider keine zwei ATmega.
Code:
/* Es gibt hier keinen Slave oder Master. Beide können beides werden. Es wird jeweils der SS_Master 
   Pin des einen  mit dem SS_Slave Pin des anderen verbunden. In SPI_init wird der SS_Master Pin auf
   high gesetzt womit der SS_Slave Pin(SSPin/PB4 beim ATMEga32) des anderen auf high ist. In 
   SPI_Transmitt wird der SS_Master auf low gesetzt und damit SS_Slave des anderen auch auf low. 
   Damit geht der mit SS_Slave auf low in den Interrupt. Im Interrupt werden erst die angekommenen 
   Daten dem Empfangsbuffer angehängt, dann wird wieder in den Master Modus geschaltet, außer der 
   SS_Slave Pin ist noch auf low, d.h. der aktuelle Master will noch was senden.
   Wird etwas versand geht der andere Mikrocontroller in den Interrupt
   und bei ihm geschieht das gleiche. 

*/

#include <avr/io.h>

#define SS_Master   PB3                // Pin zum Slave
#define SS_Slave    PB4                // Pin vom Slave
#define MOSI        PB5 
#define MISO        PB6
#define SCK         PB7
#define DDR_SPI     DDRB
#define PORT_SPI    PORTB
#define MAX         99           // Bytes des SPI Lesebuffers max. 255 !! 255 sind 256Bytes

volatile unsigned char data;

volatile unsigned char Buffer[MAX];      // Empfangs buffer
volatile unsigned char Buffer_count=0;   // Wieviele Bytes sind im Buffer

SIGNAL (SIG_SPI)        // SPI Recieve
{
   data=SPDR;         // SPI Datenregister in data laden.
   Buffer[Buffer_count]=data;      // data in Buffer laden
   Buffer_count++;     // 1 Byte mehr im Empfangsbuffer
   
   // Prüfen ob SS_Slave schon wieder high ist --> empfang vorbei
   if(PORT_SPI & (1<<SS_Slave))       
   {  
      // Wieder Master werden
      SPCR|=(1<<MSTR);
      // Daten versenden oder gleich raus aus Interrupt und nur wieder Master
      // SPI_Transmitt(' ');
   }
}



void SPI_Init(void)
{
   // Set MOSI and SCK output, all others input
   DDR_SPI = (1<<MOSI) | (1<<SCK) | (1<<SS_Master); 
   PORT_SPI|=(1<<SS_Master);  //SS Pin zum  SPI Slave auf high
   // SPI enable, Master enable, fck/16, SPI Interrupt enable
   SPCR= (1<<SPE) | (1<<MSTR) | (1<<SPR0) | (1<<SPIE);  
}

unsigned char SPI_getc(void)           // holt das letzte Byte seit dem letzten Aufruf
{
   unsigned char data,i=1;
   if(Buffe_count!=0)    // Neue Daten seit dem letzten Aufruf
   {      
	  data = Buffer[0];
      // Buffer neu ordnen damit letztes Byte wieder auf  Buffer[0] ist. 
      for(i;i<=Buffer_count;i++)
         Buffer[i-1]=Buffer[i];
      Buffer_count--;      // 1 Byte weniger im Empfangsbuffer      
      return data;
   }
}

void SPI_Transmitt_Raw(unsigned char data)  // Sollte im eigenen Programm nicht verwendet werden
{
    SPDR = data;
	while(!(SPSR & (1<<SPDIF))) asm volatile ("nop");
}

void SPI_putc(unsigned char data)         // Das ist die Funktion um ein Byte zu senden 
{                                           // z.B. SPI_putc('c'); --> sendet ein c
                                            // unsigend char c;
      PORT_SPI &= ~(1<<SS_Master);         // SPI_putc(c); --> sendet den Inhalt der Variable c
      SPDR = data;
      while(!(SPSR & (1<<SPDIF))) asm volatile ("nop");
      PORT_SPI|=(1<<SS_Master);   
   
}

void SPI_puts (unsigned char *s)             // sendet einen Buffer aus mehreren Bytes
{                                    // z.B.  SPI_puts("hallo");  --> ist aber blödes Beispiel mit hallo
    PORT_SPI &= ~(1<<SS_Master);    // unsigned char buffer[100];
	while (*s)                      // SPI_puts(buffer)
    {   /* so lange *s != NULL */
        SPI_Transmitt_Raw(*s);
        s++;
    }
    PORT_SPI|=(1<<SS_Master);
}
Also vielleicht hilfts dir ja weiter, wenn du kein C dann ist es näturlich schade.
Gruß Muraad