Source file: /~heha/basteln/PC/Programmiergeräte/Wild GIF12/mtp.zip/myFlash.cpp

#include <avr/io.h>
#include "myFlash.h"
#include <string.h>	// memset
#include <avr/eeprom.h>

void fattime::out2(char*s, byte v) {
 *--s=v%10+'0';
 *--s=v/10+'0';
}

fattime::operator char*() const{
 word t=(int)this<0?pgm_read_word(&time):time;
 string[15]=0;
 out2(string+15,t<<1&0x3E);	// Sekunde
 out2(string+13,t>>5&0x3F);	// Minute
 out2(string+11,t>>11);		// Stunde
 string[8]='T';
 word d=(int)this<0?pgm_read_word(&date):date;
 out2(string+8,d&0x1F);		// Tag
 out2(string+6,d>>5&0x0F);	// Monat
 out2(string+4,(d>>9)-30);	// Jahr
 out2(string+2,20);
 return string;
}
byte fattime::in2(const char*s) {
 byte r=(*s++-'0')*10;
 return r+*s++-'0';
}
void fattime::operator() (const char*s) {
 date=in2(s+2)+30<<9 | in2(s+4)<<5 | in2(s+6);
 time=in2(s+9)<<11 | in2(s+11)<<5 | in2(s+13)>>1;
}
char fattime::string[16];


void myFlash::sec_t::empty() {
 memset(data,0xFF,sizeof data);
}

byte myFlash::eer(byte a) {
 EEARL=a;
 EECR|=1;
 return EEDR;
}
void myFlash::eew(byte a, byte b) {
#if 0
 eeprom_write_byte((byte*)a,b);
#else
 EECR=0;
 EEARL=a;
 EECR|=1;
 a=EEDR;
 if (a!=b) {
//  if (a==0xFF) EECR|=0x20;	// Write Only
//  if (b==0xFF) EECR|=0x10;	// Erase Only
  EEDR=b;
  EECR|=4;
  EECR|=2;
  while (EECR&2);
 }
#endif
}
/* Um Flash-Speicher zu beschreiben benötigt jegliches AVR-Anwendungsprogramm,
welches auf einem Controller mit Urlader-Unterstützung läuft, die Hilfe des Urladers!
Denn der "spm"-Befehl kann/darf nur im Urlader-Bereich ausgeführt werden.
Gibt es keinen Urlader, muss "spm" im Flash ganz hinten angeordnet werden.
Aber „kein Urlader“ ist für einen ATmega32U4 oder einen Arduino wenig sinnvoll.

Daraus folgt, dass für ein Anwendungsprogramm Flash-Datenspeicher
weder ganz vorn [Interruptvektoren] noch ganz hinten ["spm"-Befehl]
liegen kann, sondern immer dazwischen.

Hier ist es der Urlader <ubaboot>. Da <ubaboot> auf Kürze optimiert ist,
gibt es keinen Platz für standardisierte Eintrittspunkte.
Stattdessen wird <do_spm> und <do_spm_rwwsre> verwendet, die Adresse hart kodiert.
Da ein Rücksprung aus <do_spm> in das Anwendungsprogramm bei "erase" und "write" crasht,
muss auf dem Stack ein Rücksprung zu <do_spm_rwwsre> zusammengebastelt werden,
um dazu im Urlader-Bereich zu verweilen.
Die Adresse von <do_spm> wird im makefile als Linker-Symbol übergeben,
<do_spm_rwwsre> ist stets 1 Instruktion davor.

Alle diese Macken entfallen bei Controllern ohne Urlader-Unterstützung,
etwa alle ATtiny und bspw. ATmega48. Aber die haben nur wenig Flash.

Das Anwendungsprogramm kann sich selbst überschreiben, das muss es selbst prüfen;
der Urlader bleibt durch ein Sperrbit vor Überschreiben geschützt.
*/

// Nur Aufruf aus Assembler! Nicht inlinen!
// do_spm mit anschließendem Rücksetzen des RWW-Sperrbits, alles im Urlader
// Interrupts müssen gesperrt sein, Watchdog wird (in <ubaboot>) beruhigt.
// PE: R19 = Operation für SPMCR (3 = löschen, 5 = schreiben)
// VR: R0, R18
// Fürs Füllen (R19 = 1) kann man dospm aufrufen.
extern "C" void dospm2() __attribute__((naked));
void dospm2() {
 asm(
"	ldi	r18,lo8(pm(do_spm-2))	\n"	// Zeiger auf RWW-Enable-Funktion
"	push	r18			\n"
"	ldi	r18,hi8(pm(do_spm-2))	\n"
"	push	r18			\n"
"dospm:	jmp	do_spm			\n");
}

void myFlash::flashwrite(byte sec, const void*b) {
  asm(
"	ldi	r19,3	\n"	// page erase
"	rcall	dospm2	\n"
"	ldi	r19,1	\n"	// page fill
"	ldi	r18,64	\n"
"1:	ld	r0,X+	\n"
"	ld	r1,X+	\n"
"	rcall	dospm	\n"
"	adiw	ZL,2	\n"
"	dec	r18	\n"
"	brne	1b	\n"
"	sbiw	ZL,2	\n"
"	ldi	r19,5	\n"	// page write
"	rcall	dospm2	\n"
"	clr	r1	\n"
  ::"z"(sec<<SEC_SH),"x"(b):"r18","r19");
}

// b = Adresse vorheriger FAT- oder Verzeichniseintrag
void myFlash::reserve(byte b, byte needsec) {
 for (byte c=FSTART; needsec; c++) if (eer(c)==0xFF) {
  eew(b,c);		// Freien Platz im Flash finden und reservieren
  b=c;
  --needsec;
 }
 eew(b,0xFE);		// „Letztes Glied der Kette“ eintragen
}

byte myFlash::lastWrittenPage;
byte myFlash::createdDirEnt;

// Die Länge muss hier bekannt sein!
bool myFlash::Create(const sec_t*s) {
 if (s->dirent.size>getFreeSpace()+(1<<SEC_SH)) return false;	// Kein Platz im Dateisystem
 byte a;
 for (a=1;a<FSTART;a++) if (eer(a)==0xFF) goto found1;	// Freien Platz im Wurzelverzeichnis suchen
 return false;
found1:
 byte needsec=(word)s->dirent.size+(2<<SEC_SH)-1 >> SEC_SH;	// mindestens 1 Sektor für den Verzeichniseintrag
 reserve(a,needsec);
 createdDirEnt=a;
 byte x=eer(a);
 flashwrite(x,s->data);
 lastWrittenPage=x;
 return true;
}
bool myFlash::writeNextPage(const sec_t*s) {
 byte x=lastWrittenPage;
 if (x<FSTART || x>=FEND1) return false;
 x=eer(x);	// Nächster Datensektor (bereits reserviert)
 if (x<FSTART || x>=FEND1) return false;
 flashwrite(x,s->data);
 lastWrittenPage=x;
 return true;
}

bool myFlash::DeleteObject(byte handle) {
 for(;;){
  byte next=eer(handle);
  eew(handle,0xFF);			// Freispeicher daraus machen
  if (next>=FEND1) return true;		// Ende der Kette erreicht
  if (next<FSTART) return false;	// Fehler!
  handle=next;
 }
}
Detected encoding: UTF-80