Source file: /~heha/basteln/Haus/Telefon/Impulswahl→DTMF/mfv.zip/mfv3c/mfv3c.c

/* Programm für ATtiny45, „h#s“ Henrik Haftmann, TU Chemnitz
 * tabsize = 8, encoding = utf-8 (ohne Windows-Header BOM)
 090331	Erster Anfang als mfv2.c
+190531	CLIP funktioniert
~190610	CLIP funktioniert nicht am Telekom-Anschluss
+190617	phoneParallel() — Tiefentladeschutz für Elko
?190626	Nummer des Anrufers kann immer noch nicht mit 7³ gespeichert werden
 210203	Ausgehend von mfv2c Umbau auf Vorsatzgerät mit umgepoltem Telefon an b und W2
 */
#include <avr/io.h>
#include <avr/pgmspace.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <avr/wdt.h> 
#include <util/delay.h>
#include <avr/fuse.h>
#include <avr/signature.h>
#include <string.h>

FUSES={
 0x5C,	// Taktteiler /8, kein Taktausgang, schnellster Startup, Resonator 3..8 MHz
#ifdef USEBOD
# if (F_CPU>4000000)
 0x55,	// kein RESET, EEPROM-Inhalt behalten, Brownout bei 2,7 V
# else
 0x56,	// kein RESET, EEPROM-Inhalt behalten, Brownout bei 1,8 V
# endif
#else
 0x57,	// kein RESET, EEPROM-Inhalt behalten, kein Brownout
#endif
 0xFF,	// Keine Selbstprogrammierung
};	// {Startup: Reset = 272 CK + 64 ms (64068 µs), PowerDown = 258 CK (64 µs)}

/************
 * Hardware *
 ************/
/*
Anschlüsse:
PB0	(5)	-	Wählscheiben-Kontakt "nsi" = Telefonleitung a, 1 = unterbrochen
PB1	(6)	OC1A	PWM-Tonausgang, reichlich (125 kHz) Trägerfrequenz
PB2	(7)	ADC1	Amtsader La = Telefonader b, über 680 kΩ zur CLIP-Dekodierung,
			Detektierung von "nsa" Telefon-Kurzschluss
			sowie Stromversorgung im aufgelegten Zustand
			inklusive RC-Hochpass 33 kΩ + 1 nF (optional)
PB3	(2)	XIN	Resonator 4 MHz
PB4	(3)	XOUT	Resonator 4 MHz
PB5	(1)	!RESET	Erdtaste (optional) / Debug-Ausgang
GND	(4)		Telefonader W2 = Anschluss für Zweitwecker
Ucc	(8)		Amtsader Lb; Z-Diode 5,6 V und Elko ≥ 470 µF nach GND

Die Erdtaste ist optional und ermöglicht einen schnellen Weg zum Rückruf.
(Sonst ist es die 9 in der Shift-Ebene, kurz 9¹)

Da man die Zeit zum Festhalten am Fingeranschlag nicht gut abschätzen kann,
ist nur eine Shift-Ebene vorgesehen.
Mit einer Bediengestik ähnlich zu HolgerK (und Vorsatzgerät mit ATtiny2313)
verbleiben nur 8 Kurzwahlspeicher.

Der EEPROM des AVR ermöglicht das Abspeichern von Nummern zur Kurzwahl.
Sonderfunktionen durch Wählen und >1 s am Finger-Anschlag halten:
 1¹..8¹	Kurzwahl
 9¹	Wahlwiederholung oder Rückruf
 0¹	Kurzwahl speichern ODER '*'/'#' wählen, danach
	1..8	Kurzwahl-Speicherplatz, Nummer, auflegen
	9	'*' wählen
	0	'#' wählen

Die Speicherfunktion bei 0¹ hängt die danach gewählten Ziffern
an vorher gewählte Ziffern an. Daher ist es auch möglich,
erst zu wählen und dann zu speichern. So können auch per CLIP
eingegangene Nummern gespeichert werden, allerdings stets mit Ortsvorwahl.

Wahlen aus dem Kurzwahlspeicher gehen nicht in die Wahlwiederholung ein.
D.h. der Wahlwiederholungsspeicher merkt sich nur explizit gewählte Nummern
oder solche vom CLIP.

Folgende interne Peripherie wird verwendet:
Ports: 2 Eingabe mit Pull-Up, 1 Ausgabe via Hardware-PWM
Timer 0: Frequenzsynthese = Stetige PWM-Änderung für Timer 1
Timer 1: High-Speed-Timer mit 32-MHz-PLL: Hardware-PWM-Ausgabe, mono
Totzeitgenerator: ungenutzt
Taktgenerator: Keramikoszillator mit bei Bedarf zugeschalteter High-Speed-PLL
Power-Management: Power-Save (onhook), Idle (offhook), CPU-Taktdrosselung
Analog-Digital-Wandler: Für onhook/offhook/Klingel-Detektion und CLIP
Analogvergleicher: ungenutzt
Interrupts: Zählerüberlauf Timer 0, Watchdog-Timer, ADC fertig (nur für CLIP)
EEPROM: Nummernspeicher für Wahlwiederholung, 8 Kurzwahlen
feste Adressen
RAM: Nummernspeicher für CLIP, als Liste hintereinanderweg
Flash-Selbstprogrammierung: ungenutzt

In dieser Firmware ist die Resonatorfrequenz variabel gehalten;
getestet habe ich's aber nur mit 4 MHz.
*/

// Signal-Zeiten in Sekunden
#define TON 0.14
#define PAUSE 0.06

typedef unsigned char byte;
typedef unsigned short word;
#define NOINIT __attribute__ ((section (".noinit")))

// Sinustabelle, Mittelwert und Amplitude = 73
// Mit der gewählten Amplitude läuft die Signal-Addition
// mit 100 % (hoher Ton) + 75 % (tiefer Ton) geradeso nicht über.
static PROGMEM const byte SinTab[256]={
 73, 75, 77, 78, 80, 82, 84, 85, 87, 89, 91, 92, 94, 96, 98, 99,
101,103,104,106,107,109,111,112,114,115,116,118,119,121,122,123,
125,126,127,128,129,131,132,133,134,135,136,137,137,138,139,140,
140,141,142,142,143,143,144,144,145,145,145,145,146,146,146,146,
146,146,146,146,146,145,145,145,145,144,144,143,143,142,142,141,
140,140,139,138,137,137,136,135,134,133,132,131,129,128,127,126,
125,123,122,121,119,118,116,115,114,112,111,109,107,106,104,103,
101, 99, 98, 96, 94, 92, 91, 89, 87, 85, 84, 82, 80, 78, 77, 75,
 73, 71, 69, 68, 66, 64, 62, 61, 59, 57, 55, 54, 52, 50, 48, 47,
 45, 43, 42, 40, 39, 37, 35, 34, 32, 31, 30, 28, 27, 25, 24, 23,
 21, 20, 19, 18, 17, 15, 14, 13, 12, 11, 10,  9,  9,  8,  7,  6,
  6,  5,  4,  4,  3,  3,  2,  2,  1,  1,  1,  1,  0,  0,  0,  0,
  0,  0,  0,  0,  0,  1,  1,  1,  1,  2,  2,  3,  3,  4,  4,  5,
  6,  6,  7,  8,  9,  9, 10, 11, 12, 13, 14, 15, 17, 18, 19, 20,
 21, 23, 24, 25, 27, 28, 30, 31, 32, 34, 35, 37, 39, 40, 42, 43,
 45, 47, 48, 50, 52, 54, 55, 57, 59, 61, 62, 64, 66, 68, 69, 71};

#if F_CPU < 6000000
# define DIVSH 7	// 128 Takte pro Timer-Interrupt
// Heruntergeteilter CPU-Takt = 24..48 kHz (Resonator 3..6 MHz)
#else
# define DIVSH 8	// 256 Takte pro Timer-Interrupt
// Heruntergeteilter CPU-Takt = 24..48 kHz (Resonator 6..12 MHz)
#endif

#define FREQ(x) ((x)*65536.0*(1<<DIVSH)/F_CPU+0.5)
// DDS-Inkremente = Additionswerte auf Phasenwert alle F_CPU/(1<<DIVSH)
static PROGMEM const word Frequencies[20] = {
 FREQ( 697),
 FREQ( 770),
 FREQ( 852),
 FREQ( 941),
 FREQ(1209),
 FREQ(1336),
 FREQ(1477),
 FREQ(1633),
 FREQ(1046),	//c³
 FREQ(1109),
 FREQ(1175),	//d³
 FREQ(1245),
 FREQ(1319),	//e³
 FREQ(1397),	//f³ (Halbtonschritt)
 FREQ(1480),
 FREQ(1568),	//g³
 FREQ(1661),
 FREQ(1760),	//a³
 FREQ(1865),
 FREQ(1975),	//h³
};

// Funktionsprinzip: DDS = Digitale Frequenzsynthese
volatile register word addA	asm("r8");
volatile register word phaA	asm("r10");	// hoher Ton
volatile register word addB	asm("r12");
volatile register word phaB	asm("r14");	// tiefer Ton

volatile register byte buf_r	asm("r7");	// Puffer-Lesezeiger
volatile register byte anrufe	asm("r6");	// Anrufzähler
volatile register byte nsi	asm("r5");	// Nummernschaltkontakt
volatile register byte pinb	asm("r4");	// reflektiert PINB mit diesen Bits:
// 0	PB0	Nummernschalter "nsi", low-aktiv
// 1	PB1	Klingelwechselspannung (nur aufgelegt)
// 2	(PB2)	abgehoben: A/D-Wandler liefert mehr als 75 %
// 3		nsa-Telefonkurzschluss: A/D-Wandler liefert mehr als 88 % (nur abgehoben)
// 5	PB5	Erdtaste, low-aktiv, optional
// 7:6		Zähler für nsa-Telefonkurzschluss
// Dabei ist PINB-Digitaleingabe auf den Bits 2..4 gesperrt via DIDR0
volatile register byte opinb	asm("r3");	// Um Veränderungen an pinb zu erkennen
volatile register byte T_on	asm("r2");	// Rest-Ton/Pausenlänge, in 16 ms
#define Flags GPIOR0		// Bits für NSK() u.a.
//	7	Watchdog-Interrupt: 16 ms vergangen
//	6	Anruf beginnt: Wenn's klingelt oder gewählt wird
//	3	0¹ = Speichermodus, Folgeziffern nicht wählen
//	2	Beim Auflegen speichern
//	0	Shift-Ebene = längerer dem 1. nsi-Impuls vorausgehender Telefon-Kurzschluss
#define wend  GPIOR1		// Zeitzähler für Telefonkurzschluss = nsa
#define Zeit8 GPIOR2		// Zeitzähler für nsi
static word onto;		// Zeitzähler für Wahl bei aufgelegtem Telefonhörer

//static byte Zeit16;	// Zeitzähler zum Abspeichern der letzten Rufnummer (für Wahlwiederholung)
static byte savepos;	// Speicherplatz für Rufnummer, wird bei mfv3c der Rufnummer vorausgewählt!

static byte wahl[11];		// Puffer für Wählziffern
#define buf_w wahl[0]
// 1. Byte = Füllstand (buf_w)
// '*'=14, '#'=15, wie im EEPROM-Kurzwahlspeicher

extern void PutNib(byte*z,byte idx,char v);

// Puffer füllen, c=0..15
static void PutBuf(char c) {
 Flags|=0x04;	// Beim Auflegen speichern
 if (buf_w<20) PutNib(wahl+1,buf_w++,c);
}

extern char GetNib(const byte*z,byte idx);

// Puffer lesen, nicht aufrufen wenn leer!
// Aufruf mit freigegeben Interrupts (aus Hauptschleife) OK
static char GetBuf(void) {
 return GetNib(wahl+1,buf_r++);
}

extern byte x10(byte);

/******************
 * EEPROM-Zugriff *
 ******************/
// Um im EEPROM möglichst viele Kurzwahlen unterbringen zu können,
// werden die Ziffern nibbleweise gespeichert, das verdoppelt die Kapazität,
// erschwert aber die Verarbeitung.
// Die Kurzwahlen liegen allesamt an festen Adressen.

//Belegung des EEPROM:		Index	Kurzwahl
//x00	10	Letzte Nummer	0	9¹
//x0A	10	K1		1	1¹
//x14	10	K2		2	2¹
//x1E	10	K3		3	3¹
//x28	10	K4		4	4¹
//x32	10	K5		5	5¹
//x3C	10	K6		6	6¹
//x46	10	K7		7	7¹
//x50	10	K8		8	8¹
//x5A	160	frei
//xFA	1	Schwellwert für Telefon abgehoben (typ. 0xC0)
//xFB	1	Schwellwert zur Erkennung von nsa-Kurzschluss (typ. 0xE0)
//xFC	1	Anzahl entgangener Anrufe
//xFD	1	Abhebezähler
//xFE	1	Watchdog-Timeout-Zähler (= kein CLIP)
//xFF	1	Reset-Zähler
//x1E0	32	CLIP-Daten (nur ATtiny85)

static void eewait(void) {
 while (EECR&2);
}
static byte eeread(void) {
 EECR|=1;
 return EEDR;
}
static void eewrite(void) {
 cli();
 EECR|=4;
 EECR|=2;
 sei();
}
static void eechange(byte b) {
 byte k=eeread();
 if (k!=b) {
  if (b==0xFF) EECR|=0x10;	// erase only
  else if (k==0xFF) EECR|=0x20;	// write only
  EEDR=b;
  eewrite();
  eewait();
  EECR=0;
 }
}


// Speichern der Nummer ab EEARL im kopflosen Format (FF-terminiert)
static void numSave(byte*z) {
 byte i=*z++;
// 1. Ziffernpuffer in ganzer Länge mit 0x0F auffüllen,
// mit dem Sonderfall '#' am Ende, dann 0x0D als 'falsches' Ende
 if (i && i!=20 && GetNib(z,i-1)==0x0F) PutNib(z,i++,0x0D);
 while (i!=20) PutNib(z,i++,0x0F);
// 2. Komplette Nummer speichern (nur Änderungen)
 for (i=0;i<10;i++) {
  eechange(z[i]);
  EEARL++;
 }
}


// <max> muss gerade sein!
static void numLoad(byte*z,byte max) {
 z++;
// 1. Nibbles vom EEPROM laden
 byte i;
 for (i=0;i<max>>1;i++) {
  z[i]=eeread();
  EEARL++;
 }
// 2. tatsächliche Rufnummern-Länge ermitteln
 for (i=max;i;) {
  char c=GetNib(z,--i);
  if (c!=0x0F) {
   if (c==0x0D && i && GetNib(z,i-1)==0x0F) --i;
   i++;
   break;
  }
 }
 *--z=i;	// 0..max möglich
}

/****************************
 * Rückruf-Liste bearbeiten *
 ****************************/
// Organisation der Rückruf-Liste:
// Nahtlose Aneinanderreihung entgangener und noch nicht "abgearbeiteter" Anrufe
// 1 Byte Länge (5 Bit) + Grund der Null-Länge (2 Bit):
// 0x00 = kein oder fehlerhaft empfangenes CLIP (Prüfsumme wird nicht ausgewertet)
// 0x20 = Grund 'O' = keine Durchleitung
// 0x40 = Grund 'P' = unterdrückt vom Anrufer
// 0x60 = sonstiger Grund
// Darauf folgen <Länge> Hex-Nibbles Rufnummer.
// Die gespeicherte Ortsvorwahl wird bei Gleichheit abgeschnitten.
// Bei ungerader Länge folgt ein ungenutztes Nibble (0) zur Byteausrichtung
// Da der Puffer mit Null initialisiert ist, führt fehlendes CLIP
// automatisch zum passenden Grund.
// Bei drohendem Pufferüberlauf wird nicht mehr gespeichert.
// Der Puffer reicht für durchschnittlich 20 Anrufe.
static byte lea[100];	// Liste entgangener Anrufe

static byte ealen(byte l) {	// Längen-Byte in Byte-Länge der Nummer
 return ((l&0x1F)+1>>1)+1;
}

static byte*eaindex(byte i) {	// Zum gegebenen Index „vorspulen“
 byte*z=lea;
 if (i) do z+=ealen(*z); while(--i);
 return z;
}

static byte IstRueckruf(const byte*z) {	// liefert buf_w wenn die aktuelle Nummer 
 if (*z++!=buf_w) return 0;
 byte i;
 for(i=0; i<buf_w; i++) if (GetNib(wahl+1,i)!=GetNib(z,i)) return 0;
 return buf_w;
}

// <clipbuf> zeigt auf 2 (Nummer des Anrufenden) oder 4 (Grund für das Fehlen)
static void AppendClip(const byte*clipbuf) {
// <anrufe> noch 0 beim 1. Klingeln
 byte*z=eaindex(anrufe);
 byte type=*clipbuf++&0x1F;
 byte len=*clipbuf++&0x1F;	// Nicht die wackligen höheren Bits beachten
 if (z+1+ealen(len)>=lea+sizeof lea) return; // Überlauf (in den Stack) verhindern
 byte i,j=0;
 if (type==2) {		// Rückrufnummer vorhanden
  if (len>20) return;		// Fehler
  for (i=0;i<len;i++) {
   byte c=*clipbuf++&0x1F;
   if (('0'&0x1F)<=c && c<=('9'&0x1F)) c&=0x0F;
   else if (c==('*'&0x1F)) c=0x0E;
   else if (c==('#'&0x1F)) c=0x0F;
   else continue;
   PutNib(z+1,j++,c);
  }
 }else{		// keine Rückrufnummer
  if (len!=1) return;		// Fehler
  byte reason=*clipbuf;
  switch (reason) {
   case 'O': j|=0x20;	// "unavailable"
   case 'P': j|=0x40;	// "private"
   default: j|=0x60;	// unknown reason
  }
 }
 *z=j;
}

static void DelRueckruf(void) {
 byte i;
 byte*z=lea;
 for(i=0;i<anrufe;) {
  byte*e=z+ealen(*z);
  if (IstRueckruf(z)) {
   memcpy(z,e,lea+sizeof lea-e);
   --anrufe;
  }else{
   z=e;
   i+=2;
  }
 }
 z=eaindex(anrufe); 
 memset(z,0,lea+sizeof lea-z);	// „Schwanz“ des Rückrufpuffers gelöscht halten
}

// liefert Nummer der Kurzwahl (1..8), 0 für keinen Treffer
static byte FindInKurzwahl(const byte*z) {
 byte idx,i;
 for (idx=8;idx;--idx) {
  EEARL=x10(idx);
  numLoad(wahl,20);
  if (buf_w=*z) for (i=0;i<*z;i++) if (GetNib(wahl+1,i)!=GetNib(z+1,i)) goto notfound;
  break;
notfound:;
 }
 buf_w=0;
 return idx;
}


/****************************
 * Kurzwahl laden/speichern *
 ****************************/

// 10 Bytes Puffer pro Nummer (20 Ziffern)
// idx zwischen 0 (letzte Nummer) und 8 (K8)
static void KurzSpeichern(byte idx) {
 EEARL=x10(idx);	// × 10
 numSave(wahl);
}

static void KurzLaden(byte idx) {
 Flags&=~0x04;		// Nicht (als Wahlwiederholung) speichern
 EEARL=x10(idx);	// × 10
 numLoad(wahl,20);	// 0..20 Ziffern
 DelRueckruf();
 buf_r=0;	// mit dem Abspielen beginnen
}

/**************************************
 * Frequenzsynthese = Sinustonausgabe *
 **************************************/
// CLD2 = Clock Logarithmus Dualis
#if F_CPU>=8000000
# define CLD2 3	// ÷ 8 = 1..1,99 MHz
#elif F_CPU>=4000000
# define CLD2 2	// ÷ 4 = 1..1,99 MHz
#else
# define CLD2 1	// ÷ 2 = 1..1,99 MHz
#endif

extern void clock_set(byte div);

static void clock_max(void) {clock_set(0);}
static void clock_1MHz(void) {clock_set(CLD2);}
static void clock_16us(void) {clock_set(CLD2+4);}

#define WDT_SEC(s) (int)((s)/0.016)

static void tonEin(void) {
 clock_max();
 PLLCSR = 0x82;		// Low-Speed-Modus
 _delay_us(100);
 while (!(PLLCSR&1));
 PLLCSR = 0x87;
 TCCR1  = 0x61;
 OCR0A  = (1<<DIVSH)-1;	// rund 32 kHz
 TIMSK  = 0x10;		// Compare-Interrupt
 TCCR0A = 0x02;		// Timer0: CTC
 TCCR0B = 0x01;		// Timer0 mit Vorteiler 1 starten
 T_on=WDT_SEC(TON+PAUSE);// Länge setzen
 DDRB  |= 0x02;		// Ausgang aktivieren
}

static void tonAus(void) {
 DDRB  &=~0x02;		// Ausgang hochohmig
 TCCR0B = 0;		// Timer0 anhalten
 TCCR1  = 0;		// Timer1 aus
 PLLCSR = 0;		// PLL aus
 clock_1MHz();
}

// Startet Wählton für Ziffer z ("*"=14, "#"=15)
static void StartDTMF(char z) {
// z&=15;		// sollte nie vorkommen
 if (MCUCR&0x10) return;	// niemals im aufgelegten Zustand
 if (!(Flags&0x40)) {
  Flags|=0x40;
  if (anrufe) --anrufe;	// Anrufliste abbauen
 }
 static PROGMEM const byte Nr2HiLo[16]={
//   0    1    2    3    4    5    6    7    8    9    A    B    C    D    *    #
  0x53,0x40,0x50,0x60,0x41,0x51,0x61,0x42,0x52,0x62,0x70,0x71,0x72,0x73,0x43,0x63};
// High-Nibble  4  5  6  7
// Low-Nibble ┌───────────
//	    0 │ 1  2  3  A
//	    1 │ 4  5  6  B
//	    2 │ 7  8  9  C
//	    3 │ *  0  #  D
 byte i=pgm_read_byte(Nr2HiLo+z);		// umrechnen in die 2 Frequenzen
 addA=pgm_read_word(Frequencies+(i>>4));	// hoher Ton im High-Nibble
 addB=pgm_read_word(Frequencies+(i&15));	// tiefer Ton im Low-Nibble
 tonEin();
}

// Startet Hinweiston, z=0 für c³
static void StartTon(char z) {
 if (MCUCR&0x10) return;	// niemals im aufgelegten Zustand
 char okt=0;			// Oktave
 while (z<0) {z+=12; --okt;}
 while (z>=12) {z-=12; ++okt;}	// jetzt 0<=z<12
 addA=pgm_read_word(Frequencies+8+z);
 if (okt<0) addA>>=-okt;
 else if (okt>0) addA<<=okt;
 addB=addA;
 phaB=phaA;			// addieren lassen
 tonEin();
}

// Kernroutine der Frequenzsynthese
// Aufruf mit F_CPU/256 oder F_CPU/128, 20..40 kHz, reicht für HiFi-Ton
// Mit F_CPU/64 kommt der Controller nicht mehr hinterher,
// obwohl noch Rechenleistung frei sein müsste.
ISR(TIM0_COMPA_vect) {
// Tonlänge nominell 70 ms, Pause 30 ms
// T_on>=0 = Tonausgabe, sonst Pause
// if (T_on>=(byte)WDT_SEC(PAUSE)) {
#if 0
  byte a=pgm_read_byte(SinTab+((phaA+=addA)>>8));
  byte b=pgm_read_byte(SinTab+((phaB+=addB)>>8));
  OCR1A=a+b-(b>>2);		// hier ohne Runden
#else
  asm(
"	add	r10,r8	\n"
"	adc	r11,r9	\n"
"	add	%A1,r11	\n"
"	adc	%B1,r1	\n"
"	lpm		\n"
"	sub	%A1,r11	\n"	// ZH:ZL wiederherstellen
"	sbc	%B1,r1	\n"
"	add	r14,r12	\n"
"	adc	r15,r13	\n"
"	add	%A1,r15	\n"
"	adc	%B1,r1	\n"
"	lpm	r1,z	\n"	// gcc compiliert fäschlicherweise "lpm r1,Z+"
"	add	r0,r1	\n"
"	lsr	r1	\n"	// (kompensiert Kabeldämpfung für höhere Frequenzen)
"	lsr	r1	\n"
"	sbc	r0,r1	\n"	// "sbc" ist der Trick fürs Abrunden
"	clr	r1	\n"	// __zero_reg__ wiederherstellen
"	out	%0,r0	\n"
  ::"I" (_SFR_IO_ADDR(OCR1A)),"z"(SinTab):"1");
#endif
// }
}

/***********************************
 * Nummernschaltkontakt-Auswertung *
 ***********************************/
// Abarbeitung nach dem LIFO-Prinzip
static void Rueckruf(void) {
 byte *z,l,i=anrufe;
 for (;;) {
  if (!i) {
   if (!(DDRB&2)) StartTon(-6);
   return;		// Nichts (mehr) rückzurufen
  }
  z=eaindex(--i);	// Zum letzten Rückruf vorspulen
  l=*z&0x1F;
  if (l) break;
  *z=0;			// Unmögliche Rückrufe von hinten allesamt löschen
  --anrufe;
  if (!(DDRB&2)) StartTon(-12);	// Rückruf (der letzten Anrufe) nicht möglich
 }
 memcpy(wahl,z,11);
 DelRueckruf();
 KurzSpeichern(0);
 buf_r=0;	// mit dem Abspielen beginnen
}

// Nummernschaltkontakt an PB0 (sowie Erdtaste an PB5 auswerten)
// Aufruf alle 16 ms per Watchdog-Interrupt aus onhook- oder offhook-Schleife
static void NSK(void) {
 Flags&=~0x80;
 byte n=nsi,z=Zeit8;
 if (!(pinb&0x20) && opinb&0x20) Flags|=1;	// Erdtaste aktiviert Shift-Ebene
 if (pinb&0x20 && !(opinb&0x20) && !(pinb&8)) goto neun;	// loslassen ohne Wählscheibenbetätigung
 if (pinb&1 && !(opinb&1)) {	// Steigende Flanke: nsi unterbrochen
  ++n;			// Lo-Hi-Flanken zählen
  onto=WDT_SEC(30);	// Globales Wähl-Timeout setzen (nur bei aufgelegtem Hörer wirksam)
//TODO: A/D-Wandler-Wert aufnehmen, das ist ungefähr die richtige Schwelle für Kurzschluss
 }else if (!(pinb&1) && opinb&1) {	// Fallende Flanke
  z=WDT_SEC(0.2);
 }else if (z && !--z && n) {	// keine Flanke: Timeout und Nummernscheibe abgelaufen?
  if (n>10) n=10;	// begrenzen
// Eigentliche Aktion
  if (Flags&8) {	// 0¹ vorgewählt?
   if (savepos) {	// Speicherplatz 1..8 angegeben?
    if (Flags&1) {	// Nochmal mit langem Halten?
     if (n<=4) n+=9;	// 'A' bis 'D' einspeichern
     else if (n>=9) n+=4;// '*' oder '#' einspeichern
    }else{	// Bei den Ziffern 5,6,7,8 bleibt langes Halten folgenlos
     if (n==10) n=0;
    }
    PutBuf(n);
    buf_r=buf_w;	// nicht ertönen lassen
    StartTon(4);
   }else{		// Langes Halten der Ziffer nach 0¹ folgenlos
    if (n>=9) PutBuf(n+4);	// '*' oder '#' wählen
    else{
     savepos=n;
     StartTon(2);
    }
   }
  }else{		// kein Speichermodus
   if (Flags&1) switch (n) {
    case 9: neun: if (anrufe) Rueckruf(); else KurzLaden(0); break;
    case 10: Flags|=8; StartTon(0); if (anrufe) Flags|=4; break;	// Nummer einspeichern: Nummer + Ziffer folgen
    default: KurzLaden(n);
   }else{		// normaler Wählvorgang
    if (n==10) n=0;
    PutBuf(n);
   }
  }
  Flags&=~1;		// Shift-Ebene verlassen
  DDRB &=~0x20;
  PORTB|= 0x20;		// Kontroll-LED ausschalten
  n=0;
 }
 Zeit8=z; nsi=n;
}

// Bei Ablauf des Watchdog-Timers (meist 16 ms) wird die CPU geweckt
// und das Bit 7 im Flag-Register (GPIOR0) gesetzt.
ISR(WDT_vect,ISR_NAKED) {
 Flags|=0x80;
 reti();
}


static byte offhooklevel NOINIT;	// High bei 75 %
static byte nsalevel NOINIT;	// Telefonkurzschluss bei 1/(1+40/680) = 93%
// Laut Datenblatt: min. 20 kΩ → 1/(1+20/680) = 97% => 0xF8
//		    max. 50 kΩ → 1/(1+50/680) = 93% => 0xEE
// Gemessen; Mittelwert 35? kΩ → 1/(1+35/680) = 95% => 0xF3
// Bei nur 5 V über dem Telefon ist die Datenblatt-Streuung des Pullups zu groß,
// da sollte dynamisch angepasst werden:
// A/D-Wert während nsi-Impulse hernehmen und einige Stufen darunter ansetzen
// sollte reichen.

// Da einige Zeit vergangen ist, seit die Pullups aktiviert wurden,
// wird zunächst PINB eingelesen und der Analogwert an PB2 ausgewertet.
// Dann wird per Watchdog fest 16 ms gewartet.
// In beiden Fällen mit abgeschaltetem A/D-Wandler und minimalem Stromverbrauch:
// Durch maximale(!) Oszillatorfrequenz und kürzester Hochlaufzeit.
// Heraus kommt die Funktion mit 1 MHz CPU-Takt, aktivierten Pullups
// und mit gestartetem A/D-Wandler, immer an PB2,
// sowie ausgewertetem Nummernschalter.
// Nicht aufrufen während Tonausgabe läuft!
static void waitforwatchdog(void) {
 opinb = pinb;		// PB0 einlesen, nachdem sich die Pegel eingeschwungen haben
 pinb  = PINB;
 GIFR  = 0x20;		// Anhängigen Pegelwechsel-Interrupt wegnehmen
 while (!(ADCSRA&0x10));	// warten auf Ende, kann bis zu 50 µs dauern
 if (ADCH>=offhooklevel) pinb|=4;	// High bei 75 %
 ADCSRA= 0;		// ADC abschalten — sonst gibt's zusätzlichen Stromverbrauch.
 PORTB = 0x02;		// Pullups aus außer an PB1
 DDRB |= 0x04;		// Floating-Eingang auf Low festnageln, um Gateschutzdiode zu schonen
 clock_max();		// kürzestmögliche Hochlaufzeit
#ifdef USEBOD
 MCUCR = 0xB4;
 MCUCR = 0xB0;		// PowerDown ohne BOD
#endif
 sleep_cpu();		// Oszillator aus bis zum nächsten Watchdog-Interrupt
 DDRB &=~0x04;		// dann umgehend PB2 = Pin 7 zur Messung vorbereiten
 PORTB|= 0x04;
 clock_16us();		// 16 µs pro Takt, gaanz langsam (4 × 16 µs)
// Die Unterprogrammaufrufe genügen zur Verzögerung.
// Warten mit dem A/D-Konverter, bis sich der Pegel an Pin 7 stabilisiert hat.
// Der Kondensator wirkt als Tiefpass für den ansonsten springenden Pegel an PB2.
 clock_1MHz();		// (10 × 16 µs)
 DDRB  = 0;
 ADCSRA= 0xD1;		// Start mit Teiler 2: 500 kHz, 2µs, ×25 = 50 µs, einmalig
 PORTB = 0x27;		// Alle Pullups aktivieren
 NSK();			// Man kann noch während des Klingelns wählen :-)
}

/********************
 * CLIP-Dekodierung *
 ********************/

// Der Interrupt dient nur zum Aufwecken; Interrupts werden/bleiben gesperrt.
// Dieser wird nur für die CLIP-Detektion benötigt, das ist Energie sparender
// als die Auswertung von ADCSRA.ADIF, denn während der CLIP-Erkennung
// steht nur die Ladung im Elko als Energiequelle zur Verfügung.
ISR(ADC_vect,ISR_NAKED) {
 asm volatile("ret");
}

// CLIP = Caller Line Identification Presentation:
// Hier liefert das Amt bzw. die Fritzbox die Uhrzeit und die Nummer des Anrufers
// mit FSK (0 ~ 800 Hz und 1 ~ 1400 Hz) und 1200 Baud und ganz kleiner Wechselspannung.
// Dummerweise muss man ziemlich flott Frequenzen auswerten, es kommt nicht flankensynchron.
// Das hatte man wohl bei der Post wegen möglicher Gruppenlaufzeitdifferenzen so gemacht.
// Zusätzlich liefert die Fritzbox den Namen in ASCII (oder UTF-8?)
// wenn dieser im Fritzbox-Telefonbuch steht? Wird hier nicht ausgewertet.
// (Den Namen habe ich bei mir nur am ISDN-Telefon gesehen.)
static byte clip[64] NOINIT;	// letztes Byte = Prüfsumme
// CLIP-Dekodierung verlangt vom Mikrocontroller alles ab, daher ist's
// in Assembler (extern) geschrieben.

// Steckt die aktuelle Rufnummer in „lea“
static void saveClip(void) {
 byte i=2,j=clip[1]+2;
 if (j>sizeof clip) j=sizeof clip;
 do{
  if (clip[i]==2 || clip[i]==4) {
   AppendClip(clip+i);
   break;
  }else i+=clip[i+1]+2;	// Länge des unbekannten Chunks
 }while(i<j);
}

extern void acInit(void);
extern byte acMean(void);
#ifdef __AVR_ATtiny85__
static byte histo[32] NOINIT;
extern void acHisto(byte data[],byte len);
#endif
extern void acOnes(void);
extern byte acByte(void);

static void acDone(void) {
 ADMUX|= 0x20;		// Ergebnis links ausrichten um nur 8 Bit auswerten zu müssen
 ADCSRA= 0xD1;		// Start mit Ergebnis in 50 µs ohne Interrupt
 MCUCR = 0x30;		// Sleep-Modus: PowerDown
// clock_1MHz();
}

// mit 1 MHz CPU-Takt
static void clipRead(void) {
// STELLSCHRAUBE, die entscheidend für die nötige Kapazität von C1 ist
 byte i=15;		// 32 ist am Thomson-Modem erprobt.
// Laut Standard sind es 250 ms nach dem Klingeln. Daher Minimum = 5.
// Kleinere Zahlen fressen mehr Zeit und mehr Strom für die Detektierung
 do{
  waitforwatchdog();	// je 16 ms
  if (GIFR&0x20) return;// Klingelwechselspannung
  if (pinb&4) return;	// Klingeln oder Abheben
 }while (--i);		// weitere ms energiesparend warten
#ifdef __AVR_ATtiny85__
 memset(histo,0,sizeof histo);
 memset(clip,0,sizeof clip);
#endif
 cli();
 wdt_reset();
// STELLSCHRAUBE, die entscheidend für die nötige Kapazität von C1 ist
 WDTCR = 0x1E;
 WDTCR = 0x0E;		// Watchdog mit Reset, 1 s, bei 1200 Baud sind das 120 Bytes
 acInit();
#ifdef __AVR_ATtiny85__
 histo[31]=acMean();
 acHisto(histo,30);
#else
 acMean();
#endif
 acOnes();
#ifdef __AVR_ATtiny85__
 histo[30]=1;
#endif
 wdt_reset();		// nach 100 langen Nulldurchgängen
 byte sum=0;		// Prüfsumme
 for (i=0;; i++) {
  byte c=acByte();
#ifdef __AVR_ATtiny85__
  histo[30]++;
#endif
  sum+=c;		// Prüfsumme nachführen
  if (i<sizeof clip-1) clip[i]=c;	// Byte abspeichern
  if (i && i==clip[1]+2) break;	// Ende der Message
 }
 clip[sizeof clip-1]=sum;	// Prüfsumme abspeichern (sollte 0 sein) aber erst mal nicht auswerten
 acDone();
 wdt_reset();
 WDTCR = 0x18;
 WDTCR = 0x40;		// Watchdog normal (gemessen 16,4 ms)
 sei();
 saveClip();
}

/************************************
 * Routinen für aufgelegten Zustand *
 ************************************/
// Handelt es sich beim Pegel an PB2 um Gleichspannung (permanent High oder auch Low) oder Klingelwechselspannung 25 Hz?
// Routine kehrt erst nach Ende des Klingelns zurück.
// Liefert Anzahl detektierter Flanken (ungefähr, in 16 ms könnten 2 Flanken bei 50 Hz sein).
static byte wait_ringend(void) {
 byte i=0,j=0;	// geht mit PB2 = High in die Routine, d.h. opinb.2 ist nach waitforwatchdog() = 1
 for(;;){
  waitforwatchdog();	// 16 ms
  if (GIFR&0x20) {
   ++j;	i=0;		// Beide Flanken an PB1 zählen
  }else{		// gleich geblieben
   if (++i==10) break;	// 16 ms × 10 = 160 ms ist ungefähr die Zeit, die das Kabelmodem braucht, um das Freizeichen aufzuschalten
  }
 }
 return j;
}

// Diese Routine wird in der Hauptschleife zuerst gerufen.
// Bei verreckter CLIP-Erkennung via Watchdog-Timeout ist Flags.6 gesetzt,
// das heißt es hat bereits 1× geklingelt
// Beim Beenden steht PORTB auf 0x27
static void onhook(void) {
 DIDR0 = 0x04;		// PB2 nur analog
 PORTB|= 0x02;		// Dort Klingeln detektieren
 PCMSK = 0x02;		// Klingelwechselspannung detektieren
 opinb = pinb = PINB;
 MCUCR = 0x30;		// Sleep-Modus: PowerDown
 ADMUX = 0x21;		// ADLAR, ADC1 = PB2
 ADCSRA= 0xD1;		// einmalig (wird in waitforwatchdog erneut gestartet)
 buf_w = buf_r = 0;	// Rufnummer tilgen
 byte rend = Flags&0x40 ? WDT_SEC(4) : 0; // Klingel-Timeout, zum Zählen verpasster Anrufe
 for(;;){
  do{
   waitforwatchdog();
   if (rend && !--rend) {
    Flags&=~0x40;	// Wenn jetzt abgehoben wird, ist vermutlich kein Gespräch aktiv.
    EEARL=252;
    eechange(++anrufe);	// Anruf verpasst
   }
   if (onto && !--onto) buf_w=0;	// Timeout für's Wählen bei aufgelegtem Telefonhörer: 255×16ms=4s
  }while (!(pinb&4) && !(GIFR&0x20));	// Bit 2 = Schaltschwelle 75% ODER Klingelwechselspannung
// Das Ansteigen der Spannung am PB2 kann 3 Gründe haben:
// * Klingeln, * Hörer abheben, * Paralleles Telefon nimmt Hörer ab
  byte j=wait_ringend();// kehrt erst nach 160 ms Ruhe zurück
  if (pinb&4) {		// Statisch > 75 % Spannung an PB2? Dann wurde abgehoben (dieses ODER paralleles Telefon!)
   if (j>=4) Flags|=0x40;// mit Klingeln: CLIP nicht (mehr) möglich, aber an onhook() laufendes Gespräch  mitteilen
   break;		// Oder ohne Klingeln: Möglicherweise nach Klingelzeichen, Flags.6 teilt das mit
  }
  if (j>=4) {		// 4 Pegelwechsel gezählt: Klingeln
   if (!(Flags&0x40)) {	// Erstes Klingeln
    Flags|=0x40;	// bemerken
    clipRead();		// CLIP einlesen, kann zum Watchdog-Reset führen (wie Exception)
   }
   rend=WDT_SEC(4);	// 4 Sekunden nach dem letzten Klingeln sei ein (verpasster) Anruf zu Ende
  }
 }
}

/************************************
 * Routinen für abgehobenen Zustand *
 ************************************/

// Testen (mit dem A/D-Wandler) ob das High an Pin 7 tatsächlich mit einer
// ausreichenden, steigenden Betriebsspannung einhergeht.
// Wenn nicht ist es ein parallel geschaltetes Telefon,
// ein gezogener Stecker oder ein totes Amt;
// dann muss mit gaaanz langer Watchdog-Spanne
// auf das Ende dieses irregulären Zustandes gewartet werden,
// um den Elko möglichst nicht zu entladen.
// Dass währenddessen keine Wahl bei aufgelegtem Telefonhörer unterstützt wird
// ist kaum tragisch und erwartungsgemäß.
// Denn wenn dieser erst mal leer ist, kann der Mikrocontroller nicht mehr
// hochlaufen und bspw. Anrufe zählen.
// Das geht dann nur durch Abnehmen des Telefonhörers.

// Im Prinzip ein Designfehler des Mikrocontrollers: Es gibt keine
// Fuse-Einstellung, mit der der Mikrocontroller ab einer bestimmten Spannung
// loslegt UND mit einer deutlich niedrigeren weiterarbeiten kann (= Hysterese)
// UND wenig Strom dafür braucht; der Brown-Out-Detektor ist zu hungrig.

// Daten etwa:
// Mikrocontroller bei 3 V im PowerDown ohne Watchdog:	max. 2 µA
//				...	mit Watchdog:	max. 10 µA
//				...	dazu BrownOut:	+ 15 µA
//			... arbeitend bei 1 MHz:	500 µA
// Vom 680-kΩ-Widerstand kommen bei 45 V Amtsspannung:	60 µA

static byte adRef NOINIT;

// Nachdem der Abfall der Speisespannung bei High an PB2 (vermeintlich abgehoben,
// aber eher parallel geschaltetes Telefon abgehoben, daher keine Durchstromung)
// bemerkt wurde, mit extrem geringer Stromaufnahme in langen Intervallen testen
// und verweilen, bis dieser Zustand beendet und genügend Spannung da ist.
// Andernfalls wird der Mikrocontroller abstürzen und kommt erst durch Abnehmen
// des Telefonhörers = Herstellung des Schleifenstroms wieder hoch.
static void phoneParallel(void) {
 tonAus();
 PORTB = 0;	// Keine ohmschen Verbraucher: Keine Pullups!
 DDRB |= 0x04;	// PB2 zu Ausgang machen und festnageln: Gateschutzdiode entlasten
 DIDR0 = 0x27;
 do{
  ADCSRA= 0;	// ADC aus
  WDTCR = 0x61;	// 8 s, Maximum
#ifdef USEBOD
  MCUCR = 0xB4;
  MCUCR = 0xB0;	// Sleep-Modus: PowerDown, kein BrownOut
#else
  MCUCR = 0x30;
#endif
  sleep_cpu();	// bis zum Watchdog
  ADCSRA= 0xF4;	// Start mit Teiler 16: 16 µs, ×13 = 208 µs
  WDTCR = 0x40;	// 16 ms (für den ADC wird offenbar 1 ms benötigt!! Sonst kommt Murks weil Referenzspannung falsch.)
  MCUCR = 0x28;	// ADC-Störunterdrückung
  sleep_cpu();	// bis zum Watchdog
 }while (ADCH>=0x3E);	// 0x3E: Hysterese von 2 (gegenüber 0x40)
 DIDR0 = 0x06;	// PB2 und PB1 nicht als Digitaleingang
 DDRB  = 0;
 PORTB = 0x25;
}

// Misst mal PB2 und mal Speisespannung. Liefert Spannung an PB2, sonst 0
static byte handleADC(void) {
 byte adPin7 = 0;
 switch (ADMUX) {
  case 0x21: {		// maß PB2 bzgl. Speisespannung?
   adPin7 = ADCH;
   ADMUX  = 0x2C;	// umschalten auf Referenzspannung 1,1 V
  }break;
  case 0x2C: {		// maß Referenzspannung bzgl. Speisespannung?
   byte b = adRef;
   byte a = ADCH;
   adRef  = a;
   if (a>=0x40 && a>b) {	// Ucc < 4,4 V (Uref > Ucc × 25%) und fallend
    phoneParallel();
   }
   ADMUX = 0x21;	// umschalten auf PB2
  }break;
  default: ADMUX = 0x21;
 }
 return adPin7;
}

static byte pause,index;	// nur offhook verwendet!

// Zyklisch aufzurufen: Arbeitet alle <anrufe> ab und generiert Zweiton-Töne
static void piepAnrufe(void) {
 if (Flags&0x40) return;	// Anruf wurde entgegen genommen: Nichts tun
 if (!anrufe) return;	// Keine Anrufe seit letztem Auflegen: Nichts tun
 if (nsi) return;	// Nummernschalter zählt
 if (pinb&8) return;	// Telefonkurzschluss durch nsa: Ton wäre unhörbar
 if (buf_w) return;
 switch (--pause) {	// zunächst 256 Aufrufe nichtstun, zwischen den Pieptönen weniger
  case WDT_SEC(0.25): StartTon(0); return;
  case 0: break;
  default: return;
 }
 const byte*z=eaindex(index);
 byte l=*z;
 char f;
 if (!(l&0x1F)) f=-1-(l>>5);	// kein Rückruf möglich
 else f=FindInKurzwahl(z);
 StartTon(f);		// CLIP hörbar machen
 if (++index>=anrufe) index=0;
 else pause=WDT_SEC(0.5);
}

static void nsa(void) {
 byte w=wend;
 if (!(opinb&8)) w=WDT_SEC(1.5);	// starte Zeitgeber
 else if (w && !--w) {
  Flags|= 1;		// Shift-Ebene startet
  PORTB&=~0x20;
  DDRB |= 0x20;	// Kontroll-LED an Pin 1, die auch die Erdtaste anzeigt
 }
 wend=w;
}

static void nsalevel_detect(byte a) {
 static word sum;
 static byte n;
 word s=sum;
 s+=a; if (!++n) {	// A/D-Wert des kurzgeschlossenen Telefons aufsummieren
//PORTB&=~0x20;		// LED zur Kontrolle aufblitzen lassen
//DDRB |= 0x20;
  nsalevel=(s>>8)-16;	// Schwellwert neu festlegen
  s=0;
//if (!(Flags&1)) {
// sleep_cpu();		// 16 ms
// DDRB &=~0x20;
// PORTB|= 0x20;
//}
 }
 sum=s;
}

static void offhook(void) {
 EEARL = 253;
 eechange(eeread()+1);	// Abhebe-Zähler
#ifdef __AVR_ATtiny85__
 EEARH = 1;
 EEARL = 0;
 byte i;
 for (i=0; i<sizeof clip; i++) {
  eechange(clip[i]);
  ++EEARL;
 }
 for (i=0; i<sizeof histo; i++) {
  eechange(histo[i]);
  ++EEARL;
 }
 EEARH = 0;
#elif defined(DEBUG)
 EEARL = 0xE0;
 byte i;
 for (i=0; i<28; i++) {
  eechange(clip[i]);
  ++EEARL;
 }
#endif
 DIDR0|= 0x02;		// Zusätzlich PB1 nicht nutzen
 PORTB&=~0x02;		// PB1 hochohmig, Gespräch nicht stören
 MCUCR = 0x20;		// Sleep-Modus: Idle (Timer läuft, Strom ist genug da)
 ADCSRA= 0xF1;		// /2 = 15 kHz / 13 = 1 kSa/s (reicht erstmal)
 T_on  = 0;
 byte dialpause=WDT_SEC(1);	// besser: auf Freizeichen warten
 //Bei Freizeichen müsste <anrufe> weitergezählt werden, wenn Flags.6 gesetzt!!
 byte a=anrufe;
 byte k=0;
 if (Flags&0x40 && a) {
 // Beim Abheben während des Klingelns nachgucken, ob CLIP im Rückrufspeicher liegt.
 // Wenn ja dann aus dieser Liste tilgen.
  byte*z=eaindex(a);
  byte l=*z&0x1F;
  if (l) {
   buf_r=l;
   memcpy(wahl,z,11);
   DelRueckruf();	// reduziert <anrufe>
   k=a-anrufe;		// Wenn etwas gelöscht wurde, dann piepsen.
  // Dann weiß man, dass dieser Anrufer bereits vorher versucht hatte anzurufen.
  // Den Ton hören beide Partner.
   KurzSpeichern(0);
  }
 }
 pause=WDT_SEC(1);
 index=0;
 for(;;) {		// Hauptschleife
  sleep_cpu();		// blockiert bis zu 16 ms — oder auch fast nicht bei laufender DDS
  if (Flags&0x80) {	// Watchdog-Interrupt?
   EEARL=252; eechange(anrufe);
   byte a=handleADC();	// Auch den A/D-Wandler per Watchdog-Timer abfragen.
   byte b=pinb;
   opinb=b;
   b&=0xCC;
   b|=PINB&0x21;
   if (DDRB&0x20) b|=0x20;	// Bei eingeschalteter LED „keine Erdtaste“ annehmen
   if (a) {		// effektiv Halbierung dieser Schleifendurchlaufrate
    b&=~0x0C;
    if (a>=offhooklevel) b|=4;	// High bei 75 %
    if (!(b&4) && !(opinb&4)) {pinb=b; break;}	// 2× Low durch Auflegen (> 64 ms, kein Spike)
    if (a>=nsalevel) {	// nsa?
     if (!(b&8)) {
      b+=0x40;	// viermal hintereinander
//TODO: Dieses Stückchen Kode ist zur Hölle nicht zum Laufen zu bekommen:
//b.3 ist dann immer wieder gelöscht!
      /*if (!(b&0xC0))*/ b|=8;	// Bei Überlauf
     }
    }else{
     b&=~0xC8;
    }
    if (b&1) nsalevel_detect(a);	// nsi geöffnet
   }
   if (T_on && --T_on==WDT_SEC(PAUSE)-1) tonAus();	// Tonlänge reduzieren
   pinb=b;
   if (b&8) nsa();
//   if (pinb&8 && !(opinb&8)) PINB|=0x20;
   NSK();			// Nummernschaltkontakt-Auswertung
   piepAnrufe();
   if (T_on) continue;		// Ton- oder Pausenausgabe läuft
   if (dialpause) --dialpause;
   else{
    if (k) {StartTon(k); k=0;}
    else if (buf_r!=buf_w) StartDTMF(GetBuf());	// Nächsten Ton + Pause ausgeben
   }
  }
 }
 if (T_on>=(byte)WDT_SEC(PAUSE)) tonAus();
}

/*************************************
 * Initialisierung und Hauptschleife *
 *************************************/
// Hochlauf mit 500 kHz
void __init(void) __attribute__((naked,section (".init2")));
void __init(void) {
 asm volatile("clr r1");// Stack nicht initialisieren, um gleichermaßen auf ATtiny45 und ATtiny85 lauffähig zu sein
			// SPH:SPL ist bereits mit RAMEND initialisiert
 T_on  = MCUSR;	// retten in R2
 MCUSR = 0;
 ACSR |= 0x80;		// Analogvergleicher ausschalten
 DIDR0 = 0x27;		// Digitale Eingänge (Stromfresser) aus
 sei();
 if (!(T_on&0x08)) {	// Kein RESET durch Watchdog?
  WDTCR = 0x18;
  WDTCR = 0x46;		// Watchdog-Interrupt nach 1 s
#ifdef USEBOD
  MCUCR = 0xB4;		// PowerDown
  MCUCR = 0xB0;		// Unterspannungsdetektor aus
#else
  MCUCR = 0x30;		// PowerDown
#endif
  sleep_cpu();		// 1 s warten, Speisespannung sollte steigen
 }
 WDTCR = 0x18;
 WDTCR = 0x40;		// Watchdog auf Interrupt, 16 ms = 62 Hz (einzige Interruptquelle)
}

static void hardwareInit(void) {
 eewait();
 EEARL = T_on&0x08?254:255;	// Watchdog-Reset (während CLIP-Erkennung)
#ifdef __AVR_ATtiny85__
 EEARH = 0;
#endif
 eechange(eeread()+1);	// Reset- oder Watchdog-Reset-Zähler
 EEARL = 250; byte a=eeread();
 offhooklevel = /*a!=0xFF ? a :*/ 0xC0;
 EEARL = 251; a=eeread();
 nsalevel = /*a!=0xFF ? a :*/ 0xE0;
 EEARL = 252; a=eeread();
 if (a>20) a=0;
 anrufe= a;
 buf_r = 0;		// alle übrigen Register nullsetzen
 Flags = T_on&0x08 ? 0x40 : 0;
 nsi   = 0;
 Zeit8 = 0;
}

int __attribute__((noreturn)) main(void) {
 hardwareInit();
 for(;;) {
  onhook();
  offhook();
  if (Flags&4) KurzSpeichern(savepos);
  EEARL = 250;
  eechange(offhooklevel);
  EEARL = 251;
  eechange(nsalevel);
  Flags = 0;		// Aktiven Anruf beenden
 }
}
Detected encoding: UTF-80