Die Grenze ist nicht 16K, sondern der Programmadressbereich ist 128K
Bytes (d.h. call k mit 0 <= k < 64K) bei einem AVR mit 16-bit breiten
PC.
Wenn du ein 16K AVR hast und 2 * 0x1FFE anspringst, verlässt du IMO den
16K Bereich, Es ist der PC in WORDS nicht in BYTES zu zählen, das 2* ist
zuviel.
Ne, ist schon richtig, der AVR-GCC will Byteadressen haben:
asm volatile ("call 2 * 0x1FFF" ::);
aa: 0e 94 ff 1f call 0x3ffe
Das ist aber nicht mein Problem.
Peter
Dann habe ich mich foppen lassen vom Disassembler-View im AVR Studio.
Ich dachte, wenn man links in der Ansicht den PC sieht (in +1 Schritten)
und rechts daneben 2 Instruktionsbytes, dann heisst das PC+1 geht 2
Bytes im ROM weiter. Dazu habe ich mir aus dem Instruction Set
zurechtgereimt, wieso dort die Einschränkung (call k mit 0 <= k < 64K)
steht.
Sorry.
> asm volatile ("call 2 * 0x1FFE" ::);>>> funktioniert, aber eben nur für nen 16kB AVR.
Ja. Bei den kleineren gibt's kein call.
Man könnt's aber auch in C machen:
((void(*)(void))(FLASHEND/2))();
Hier muß man's dann lustigerweise durch 2 teilen, da FLASHEND eine
Byteadresse ist, der Aufruf aber eine Word-Adresse will.
Funktioniert aber auch nur bis 128kB korrekt und benutzt aus irgendeinem
Grund immer icall.
Rolf Magnus wrote:
> Funktioniert aber auch nur bis 128kB korrekt und benutzt aus irgendeinem> Grund immer icall.
Ja, mein ATmega2561 guckt dann blöd aus der Wäsche.
Ich machs jetzt so, dann sind Mega8..2561 zufrieden und Z wird nicht
zerstört.
1
voidapicall(void)
2
{
3
asmvolatile("ldi r16, %0"::"M"((FLASHEND>>1)&0xFF));// lo
4
asmvolatile("push r16");
5
asmvolatile("ldi r16, %0"::"M"((FLASHEND>>9)&0xFF));// hi
Rolf Magnus wrote:
> Da fehlt noch ein ret, oder?
Ich denke er löst den eigentlichen jump dadurch aus,
dass er den Returnstack manipuliert.
-> Funktionsreturn macht den Jump
Karl heinz Buchegger wrote:
> Ich denke er löst den eigentlichen jump dadurch aus,> dass er den Returnstack manipuliert.> -> Funktionsreturn macht den Jump
Jau, so isses.
Die Funktion darf also nicht inlined werden.
Zur Sicherheit noch das hier drüber schreiben:
1
voidapicall(void)__attribute__((noinline));
Aufm Mega168 hab ichs getestet, läuft wie dumm.
Ich kann jetzt meine 8kB Daten nichtflüchtig speichern.
EEPROM war gestern.
Peter
>> Da fehlt noch ein ret, oder?>>> Ich denke er löst den eigentlichen jump dadurch aus,> dass er den Returnstack manipuliert.
Ja, eben, und dann muß das entsprechende ret dazu aber auch da sein.
> -> Funktionsreturn macht den Jump
Aber deren Rücksprungadresse liegt doch auch noch auf dem Stack. So
verliert er doch zwei bzw. drei Bytes Stack bei jedem apicall()-Aufruf.
Nein, denn die funktion apicall ist doch 'nur' ein jumppad/wrapper und
die API function soll doch zu dem aufrufer von apicall zurück...
Also der return von apicall nimmt die gerade auf den stack gelegen werte
und die 'system-funktion' dann die return addresse die der call von
apicall auf den stack gelegt hatte.
in der ursprünglichen variante, währen das auch 'zwei' returns die
system-function returned zu apicall und apicall zum aufrufer.
mfg.
olaf
Olaf wrote:
> @Peter:> Du soltest das register r16 vor und nachher noch sichern, das ist ja> callee-save definiert.
Danke.
Ich werds dann mal auf R22 ändern.
Die API-Routine habe ich schon auf die X,Z-Pointer geändert.
Peter
nochmal ich...
Habe nochmal alle constrains der gcc asm anweisung angesehen und
folgende lösung gefunden:
1
voidapicall(void){
2
#if (FLASHEND>0x2000)
3
asmvolatile("jmp %0"::"p"(FLASHEND&~1));
4
#else
5
asmvolatile("rjmp %0"::"p"(FLASHEND&~1));
6
#endif
7
}
Vorteil zu obiger lösung, es wird kein weiteres register benötigt und
solange kein AVR mit 4MB flash in sicht ist, brauch man auch den code
nicht weiter anfassen.
Geht mit gcc-4.2.0 und gcc-3.4.4.
Ach ja besser ist ein jump und kein call, da apicall ja 'nur' ein jump
pad ist. Solltest du die function inline declarieren, dann: s/jmp/call/
mfg.
Olaf