Hallo,
Folgendes geht ohne Angabe der Feldgröße:
char abc[]={"hallo"};
char def[]="hallo";
char *ghi={"hallo"};
char *jkl="hallo";
Folgende Beispiele gehen jedoch nicht:
char abc[][]={"compiler", "meckert"};
char def[2][]={"compiler", "meckert"};
char ghi[][9]={"compiler", "meckert"};
Wieso kann wenigstens für "char def" nicht automatisch die Feldgröße
(Stringlänge) ermittelt werden?
Das hier geht allerdings wieder ohne jegliche Feldgrößenangabe:
char *abc[] = {"so", "klappt", "es"};
printf(abc[2]); //Ausgabe: "es"
Wieso? die subtilen Regeln von C sind mir nicht klar. Vielleicht kann
mich jemand erhellen... Danke
> Wieso kann wenigstens für "char def" nicht automatisch die
Feldgröße
> (Stringlänge) ermittelt werden?
ghi funktioniert anstandslos. Die Stringlänge mehrerer Strings kann
er nicht automatisch ermitteln, aber die erste Felddimension kann
automatisch an Hand der Initializer ermittelt werden. Aber das
mehrdimensionale Array ist für Zeichenketten ohnehin keine sehr
praktikable Lösung.
> Das hier geht allerdings wieder ohne jegliche Feldgrößenangabe:> char *abc[] = {"so", "klappt", "es"};
Klar, jetzt bist du ja auch von einem mehrdimensionalen Array (of
char) übergegangen zu einem eindimensionalen (of pointer to char).
Das hat den zusätzlichen netten Effekt, dass du im Gegensatz zum
mehrdimensionalen Array nicht N-mal den Platz für den längsten String
allozierst, sondern N-mal den Platz für einen Zeiger, während die
Strings selbst ausschließlich den für ihre Darstellung notwendigen
Platz belegen.
> Wieso? die subtilen Regeln von C sind mir nicht klar. Vielleicht> kann mich jemand erhellen... Danke
Im Grunde ist das Prinzip sehr simpel:
Wenn du ein mehrdimensionales Feld anlegst, dann muss
der Compiler dass irgendwie im Speicher unterbringen.
Er macht das so, dass er die einzelnen Elemente der
2-ten Dimension einfach aneinanderhängt.
1
char Test[2][3];
2
3
Test
4
+---+---+---+---+---+---+
5
| | | | | | |
6
+---+---+---+---+---+---+
7
0 1 2 0 1 2
8
9
| | |
10
das hier und hier
11
ist Test[0] Test[1]
man sagt auch: "der letzte Index läuft am schnellsten"
Die Elemente [0][0], [0][1], [0][2] liegen also direkt
hintereinander, gefolgt von [1][0], [1][1], [1][2]
usw.
Wenn du daher auf ein Element [a][b] zugreifst, muss
der Compiler ausgehend von der Startadresse des Arrays
rechnen:
1
Adresse von [a][b] = Startadresse +
2
a * Dimensionierung der 2.ten Dimension +
3
b
Um also die Adresse berechnen zu können, muss der Compiler wissen
wie gross den die 2. te Dimension tatsächlich ist.
Bei
1
2
charTest[2][3]
wäre das die 3.
Nun hätte man natürlich für char-Arrays als Sonderfall
zulassen können, dass der Compiler die Abzählerei macht.
Schliesslich ist alles dazu Notwendige in der Initialisierung
enthalten. Ich denke aber man wollte sich einfach keinen
Sonderfall einhandeln.
Übrigens: Bei
1
char*abc[]={"so","klappt","es"};
wird etwas völlig anderes im Speicher aufgebaut.
Das sieht dann so aus
Der Thread ist zwar schon alt aber Karls Erklärung hat mich grad auch
begeistert, besonders die netten ASCII Zeichnungen^^ (hast du die per
Hand eingetippt? xD)
lg PoWl
Moin zusammen,
wie müsste ich folgendes Problem angehen ...
ein Liste von Strings in verschiedenen Sprachen:
1
constcharstr_de[][15+1]={
2
"Text in deutsch"// info
3
"hallo Welt "// hello
4
}
5
6
constcharstr_en[][15+1]={
7
"text in english"// info
8
"hello world "// hello
9
}
10
11
char**str_ptr;
12
13
(...)
14
// Initialisierung
15
if(language==deutsch){
16
str_ptr=str_de;
17
}
18
else{
19
str_ptr=str_en;
20
}
21
22
(...)
23
// main loop
24
print(str_ptr[0]);
25
print(str_ptr[1]);
Bei der Zuweisung des Pointers schimpft der Compiler, weil die Typen
nicht zusammenpassen. Wie muss der Pointer aussehen, damit ich ihn auf
die Stringliste der jeweiligen Sprache zeigen lassen kann?
Merke: Ein Pointer ist kein Array. Ein 2-D Array, so wie du es hattest,
ist daher auch kein Array von Pointern auf Strings. Daher kann str_ptr
auch kein Pointer auf einen Pointer sein. Aber du kannst deine
Textarrays so umbauen, dass es sich tatsächlich um ein Array von
Pointern handelt.
prima!
Das ist genau so, wie ich es mir vorgestellt habe:
An einer zentralen Stelle werden alle Strings gesammelt und
an einer zentralen Stelle wird über die Sprache entschieden.
Vielen Dank!
Hallo,
mein Compiler meckert bei einer einfache Initialisierung eines
mehrdimensionalen char-Arrays:
struct spielfeld
{
char koord[9][9] = {{'x','A','B','C','D','E','F','G','H'},
{'A','0','0','0','0','0','0','0','0'},
{'B','0','0','0','0','0','0','0','0'},
{'C','0','0','0','0','0','0','0','0'},
{'D','0','0','0','0','0','0','0','0'},
{'E','0','0','0','0','0','0','0','0'},
{'F','0','0','0','0','0','0','0','0'},
{'G','0','0','0','0','0','0','0','0'},
{'H','0','0','0','0','0','0','0','0'}};
};
Dabei bringt er die Meldung
"error: exspected ':', ',', ';', '}' or '__attribute__' before '='
token"
und bezieht sich auf die dritte Zeile.
Ich habe den GNU GCC Compiler. Kann mir da jemand weiterhelfen? Ich hab
absolut keine Ahnung, habe mehrmals alles abgesucht, aber diese Meldung
hilft mir garnichts. Danke!
Guten Morgen!
In meinem Projekt habe ich eine Funktion, die aus eine SD-Karte eine
Liste von ersten 16 Dateinamen ausliest und diese in einem
2D-Char-Array (Files[16][16]) abspeichert. Das klappt auch.
Jetzt wollte ich die Funktion so umschreiben, das ich diesen Array als
parameter an die Funktion übergebe.
Einzigste Methode die bei mir klappt, einen Pointer auf Files[0][0]
übergeben und jedes Byte manuell füllen. Das heisst erste Dateiname
(z.B. 6 char lang) schreibe ich in Adresse 0 bis 5 + '0' in die Adresse
6. Zweite in die Adresse 16 bis xx u.s.w.
Gibt es eine schönere Methode so einen Array zu Füllen?
MfG
Wärst du so nett, für eine neue Frage einen neuen Thread aufzumachen?
Es sei denn, deine Frage ist hier schon beantwortet.
Dann musst du nur lesen, und nicht fragen.
Das Problem habe ich gerade gelöst. Habe nochmal bei Wikibook den
Kapitel "Zeiger & Arrays" durchgelesen.
Klaus Wachtler schrieb:> Wärst du so nett, für eine neue Frage einen neuen Thread aufzumachen?
Nächstes mal mache ich!
Hallo,
ich wühl das hier nochmal raus, da es echt nicht so einfach ist die
passende Syntax zu finden.
Das sollen meine Strings sein, die über einen Zeiger erreichbar sein
sollen. Mit mehrdim. Array will ich nicht anfangen.
Es kommt nur Müll auf dem Display, weil die Zeiger vermutlich in den
Binärspeicher zeigen.
Es gibt eine warning: pointer target lost const qualifier.
Was nun?
Ich wüsste nicht was an meinem Quelltext übel ist. Der ist so dass er
seine Aufgabe effzient erfüllt. Und es passt thematisch hier rein,
deswegen erzeuge ich keinen neuen Thread.
Christian J. schrieb:> Ich wüsste nicht was an meinem Quelltext übel ist.
An dem Quelltext ist z.B. übel, dass da ein Komma fehlt. Dadurch
verrutscht Dir einiges im Array. menue[5] ist damit u.U. gar nicht
initialisiert.
P.S.
Die Warnung eliminierst Du, indem Du schreibst:
void printstrng(const char *k)
...
Schließlich handelt es sich hier um const.
Frank M. schrieb:> Christian J. schrieb:>> Ich wüsste nicht was an meinem Quelltext übel ist.>> An dem Quelltext ist z.B. übel, dass da ein Komma fehlt. Dadurch> verrutscht Dir einiges im Array. menue[5] ist damit u.U. gar nicht> initialisiert.>
Da fehlt kein Komma ;). Keiner hat gesagt, daß das Array so belegt sein
muß, wie's auf den ersten Blick im Quelltext aussieht.
Die Schleife läuft nur von 0 bis 5. Wär' da ein Komma, würde die letzte
Zeile nicht mit ausgegeben werden.
Für mich stellt sich die Frage, warum hier überhaupt ein Array verwendet
wird, das läßt doch genausogut auf einen Rutsch ausgeben?
Markus F. schrieb:> Die Schleife läuft nur von 0 bis 5. Wär' da ein Komma, würde die letzte> Zeile nicht mit ausgegeben werden.
Stimmt, Du hast recht. Trotzdem glaube ich nicht, dass genau das im
Sinne des Erfinders war. Sieht jedenfalls erstmal nicht so aus.
Das Programm läuft bei mir unter Linux einwandfrei - jedenfalls das, was
da an Auszügen gepostet wurde. Fragt sich nur, ob das verwendete Display
die putchar()-Befehle genauso umsetzt wie ein herkömmliches Terminal.
Frank M. schrieb:> Das Programm läuft bei mir unter Linux einwandfrei -
Das glaube ich, bei mir auch. Nur ist das der SDCC Compiler für den Z80
und inzwischen weiss ich, dass die warning ein Bug ist. Ich werde also
um einen Workaround nicht herum kommen. Oder mich durch den Asm Code
wühlen, der erzeugt worden ist.
Karl heinz Buchegger schrieb:> Übrigens: Bei char *abc[] = {"so", "klappt", "es"};
geht das auch mit EEMEM ?
ich grübel immer noch wie ich ohne Abzählerei die Menüstrings
platzsparend ins EEPROM bringe um dann wieder später zugreifen zu
können.
Christian J. schrieb:> Frank M. schrieb:>> Das Programm läuft bei mir unter Linux einwandfrei ->> Das glaube ich, bei mir auch. Nur ist das der SDCC Compiler für den Z80> und inzwischen weiss ich, dass die warning ein Bug ist.
Auf einem Z80?
Das glaube ich ehrlich gesagt nicht.
Denn ein Z80 unterscheidet nicht zwischen Adressbereichen. Selbst wenn
der COmpiler die Texte aufgrund ihrer Konstantheit irgendwo speziell im
Adressberecih unterbringen würde, kann sie der Code nach wie vor über
genau dieselben Befehle laden und bearbeiten, wie wenn sie im RAM liegen
würden.
(NIcht gebanktes System mal vorausgesetzt)
Joachim B. schrieb:> Karl heinz Buchegger schrieb:>> Übrigens: Bei char *abc[] = {"so", "klappt", "es"};>> geht das auch mit EEMEM ?>> ich grübel immer noch wie ich ohne Abzählerei die Menüstrings> platzsparend ins EEPROM bringe um dann wieder später zugreifen zu> können.
Müsste auch gehen.
Bei den Zugriffen musst du halt aufpassen, dass du nicht einfach
drauflos arbeiten kannst, sondern mit den eprom Funktionen arbeiten
musst, aber prinzipiell spricht da nichts dagegen.
Karl Heinz schrieb:> (NIcht gebanktes System mal vorausgesetzt)
Gerade gesehen:
Du hast ja ein komplettes Listing gepostet.
Das sieht eigentlich alles gut aus - wenn die UART grundsätzlich
funktioniert. Davon schreibst du aber nichts. Die Stringausgaben sind
soweit ich gesehen habe, die erste Ausgabe im Programm. Funktioniert die
UART nicht (falsche Baudrate oder so), dann sind deine Strings beim
Empfänger natürlich unleserlich. SChuld ist dann aber nicht die
Stringausgabe an sich und auch nicht die Verpointerung, sondern Schuld
daran ist dann, das die UART-Übertragung grundsätzlich nicht
funktioniert.
Karl Heinz schrieb:> Mal ganz anders gefragt.> Hast du schon getestet, ob putchar() bzw. UART überhaupt richtig> funktioniert?
Ja, einwandfrei. Ausgaben über prinft klappen. Ich habe alles stückweise
durchgeprüft in der Kette der Glieder bis zmum PC hin. Die Ausgabe bei
dem Beispiel, auch mit Komma ist Binärmüll auf dem Terminalprogram.
Der Hase liegt her im Peffer:
730 ;main.c:100: printstrng(menue[i]);
0205 6A [ 4] 731 ld l,d
0206 26 00 [ 7] 732 ld h,#0x00
0208 29 [11] 733 add hl, hl
0209 01r00r00 [10] 734 ld bc,#_menue
020C 09 [11] 735 add hl,bc
020D 4E [ 7] 736 ld c,(hl)
020E 23 [ 6] 737 inc hl
020F 46 [ 7] 738 ld b,(hl)
0210 D5 [11] 739 push de
0211 C5 [11] 740 push bc
0212 CDr20r00 [17] 741 call _printstrng
0215 F1 [10] 742 pop af
0216 D1 [10] 743 pop de
Üblicherweise übergeben Compiler Variablen auf dem Stack an
Unterroutinen. Und da weiss ich eben nicht genau, ob der die Adressen
der Strings, unten im Listing zu sehen wirklich richtig übermittelt. So
fit bin ich da auch´nicht drin. Vermutlich wird die Adresse in BC, DE
übergeben an _printstrng.
printstrng holt sie sich ja auch und korrigiert dann den Stack schnell
wieder:
; ---------------------------------
156 ; Function printstrng
157 ; ---------------------------------
0020 158 _printstrng_start::
0020 159 _printstrng:
160 ;driver.c:28: while ((*k) != '\0')
0020 C1 [10] 161 pop bc
0021 E1 [10] 162 pop hl
0022 E5 [11] 163 push hl
0023 C5 [11] 164 push bc
0024 165 00101$:
0024 7E [ 7] 166 ld a,(hl)
0025 B7 [ 4] 167 or a, a
0026 C8 [11] 168 ret Z
169 ;driver.c:29: putchar(*(k++));
Christian J. schrieb:> Unterroutinen. Und da weiss ich eben nicht genau, ob der die Adressen> der Strings, unten im Listing zu sehen wirklich richtig übermittelt.
Ich denke du hast recht. Da stimmt wirklich was nicht.
> So> fit bin ich da auch´nicht drin. Vermutlich wird die Adresse in BC, DE> übergeben an _printstrng.
BC
DE ist da nur Nebenschauplatz. Könnte damit zusammenhängen, dass
Calling-Konventions einzuhalten sind, die 2 Registerpärchen am Stack
vorschreiben.
Aber die Reihenfolge in der auf den Stack gepusht wird, ist tatsächlich
falsch rum.
printstrng will die Adresse offnbar in HL haben. Sieht man ganz deutlich
hier
1
00247E[7]166lda,(hl)
HL hilt sich die Funktion aber
1
0020C1[10]161popbc
2
0021E1[10]162pophl
als 2.tes vom Stack. D.h. das ist der ältere der beiden PUSH vom
Aufrufer hier
1
020F46[7]738ldb,(hl)
2
0210D5[11]739pushde
3
0211C5[11]740pushbc
4
0212CDr20r00[17]741call_printstrng
5
...
also der Wert vom PUSH DE
in DE steht aber zu diesem Zeitpunkt nicht die Adresse vom String
drinnen. Die ist in BC.
Karl Heinz schrieb:> als 2.tes vom Stack. D.h. das ist der ältere der beiden PUSH vom> Aufrufer hier>
1
>020F46[7]738ldb,(hl)
2
>0210D5[11]739pushde
3
>0211C5[11]740pushbc
4
>0212CDr20r00[17]741call_printstrng
5
>...
6
>
>> also der Wert vom PUSH DE>> in DE steht aber zu diesem Zeitpunkt nicht die Adresse vom String> drinnen. Die ist in BC.
Arg.
Kommando zurück.
Ich hab auf die Return Adresse vergessen, die liegt ja auch noch auf dem
Stack. Berücksichtigt man die auch noch, dann kriegt _printstrng den
korrekten Wert ins richtige Register.
Ja, wollte ich grd schreiben, dass das schon richtig ist :-) Bin auch
grad dran. Tja, weiter weiss ich nicht außer alles so lange umzubauen
und immer neue EEPROM zubrennen bis es klappt.
Die Addy kommt hier in BC zu liegen, etwas umständlich aber halt
Compiler.
0209 01r00r00 [10] 734 ld bc,#_menue
020C 09 [11] 735 add hl,bc
020D 4E [ 7] 736 ld c,(hl)
020E 23 [ 6] 737 inc hl
020F 46 [ 7] 738 ld b,(hl)
Funktionieren tut
char m1[] = "Text 1"
char m2[] = "Text 2"
und Call mit
printstrng((char*)m1);
Der SDCC ist bei weitem nicht bug frei!
Ein
IO_REG | = (1 << 7)
lässt ihn mit "internal compiler error" abschmieren.
Christian J. schrieb:> Tja, weiter weiss ich nicht
Da bist du nicht alleine :-)
> Die Addy kommt hier in BC zu liegen, etwas umständlich aber halt> Compiler.>> 0209 01r00r00 [10] 734 ld bc,#_menue> 020C 09 [11] 735 add hl,bc> 020D 4E [ 7] 736 ld c,(hl)> 020E 23 [ 6] 737 inc hl> 020F 46 [ 7] 738 ld b,(hl)
Ist nicht wirklich umständlich.
Du vergisst, dass es ja da noch ein Zwischenarray gibt, in dem die
Adresse des eigentlichen Strings liegt. D.h. der Compiler berechnet erst
mal an welcher Position im Pointer Array der richtige Pointer liegt (das
ist das Vorgeplänkel mit dem D Register und der Addition der Adresse von
'_menu'), holt sich dann von dort die Adresse des Strings nach BC.
Selbst wenn du das händisch in Assembler programmierst, wirst du das
nicht wesentlich anders lösen. Von daher sehe ich das nicht so, dass der
Compiler das umständlich gelöst hätte. Da muss ich den SDCC
freisprechen.
> Der SDCC ist bei weitem nicht bug frei!
Das glaub ich schon. Wird der überhaupt noch gewartet?
Aber: im geposteten Listing kann ich nichts entdecken, was da schief
laufen würde. Ich würde sagen, compilerseitig hat er den Teil richtig
umgesetzt.
Karl Heinz schrieb:> Compiler das umständlich gelöst hätte. Da muss ich den SDCC> freisprechen.>>> Der SDCC ist bei weitem nicht bug frei!>> Das glaub ich schon. Wird der überhaupt noch gewartet?>> Aber: im geposteten Listing kann ich nichts entdecken, was da schief> laufen würde. Ich würde sagen, compilerseitig hat er den Teil richtig> umgesetzt.
Letze Version die ich habe ist von 18.10.2014. Und die Buglist bei
sourceforge ist schon recht "üppig". Ansonsten aber bin ich erstaunt wie
clever er manche Sachen umsetzt. wie zb Bit-Tests. dass er merkt, dass
ein & 0x80 der Test des linken Bits ist, das rausschiebt ins Carry und
dann testet. & 0x01 schiebt er es rechts raus. Der Code ist nicht viel
größer als Asm, vielleicht 25% mehr aber pfeif drauf, dafür ist C
deutlich fixer in der Programmierung.
Jetzt kann ich ur noch versuchen den Code mal durch einen Simulator zu
jagen, denn es gibt ihn auch als compilierbaren Asm und nicht nur als
Listing.
Ist das wirklich ok? Habe mal ein
printstrg(menue[0]);
kompiliert.
Das ergibt
1
725 ;main.c:95: printstrng(menue[0]);
2
0200 2Ar00r00 [16] 726 ld hl, (#_menue + 0)
3
0203 E5 [11] 727 push hl
4
0204 CDr20r00 [17] 728 call _printstrng
5
0207 F1 [10] 729 pop af
ld hl, (#_menue + 0) = Lade HL mit der absoluten Adresse von _menue +
Index 0, weil ich ja String 0 ausgeben will.
2Ar00r00 ist der Befehl ld hl,... und die 00r00 sind Platzhalter.
1
Das kommt nochmal hier vor:
2
3
0000 829 __xinit__menue:
4
0000r61r02 830 .dw ___str_0
5
0002r78r02 831 .dw ___str_1
6
0004r9Ar02 832 .dw ___str_2
7
0006rBBr02 833 .dw ___str_3
8
0008rDDr02 834 .dw ___str_4
9
000ArF9r02 835 .dw ___str_5
10
000Cr14r03 836 .dw ___str_6
11
837 .area _CABS (ABS)
0000r61r02 + 0x100 Offset für.code könnte 0x0361 sein mit etwas
Phantasie. Da liegt der [0] String. siehe Hex Dump. Bei 0x378 liegt auch
der 2.te String und die sind in 0002r78r02 codiert.
Und die _str sind hier:
1
800 .area _CODE
2
0261 801 ___str_0:
3
0261 5A 38 30 20 53 79 802 .ascii "Z80 System is online!"
4
73 74 65 6D 20 69
5
73 20 6F 6E 6C 69
6
6E 65 21
7
0276 0A 803 .db 0x0A
8
0277 00 804 .db 0x00
Die 00 00 sind Platzhalter für den Linker. Der Hex Dump zeigt hier:
2A 02 20 = ld hl, #2002
Der String steht aber bei 0361. 0x100 muss man draufaddieren, das ist
der Code Offset.
Kompiliere ich menue[1] steht da 2A 04 20, also ein Wort weiter.
Bei 2002 liegt das RAm, ab 0x2000 ist es da.
UNd hier tauchen die 2020 wieder auf, im Map File in einer .initializer
Area. 2020 und nicht 2002 !
Sehr seltsam..... das stimmt nicht. Das HL Register wird mit einem
völlig falschen Wert geladen, mitten aus dem RAM und das dann Blödsinn
rauskommt ist klar.
Für mich sieht das schwer nach einem Bug aus. Der Source liegt mir vor,
allerding hat der 2h kompiliert und das sind hunderte Dateien in eben so
vielen Unterverzeichnissen. Keine Chance wo ich da suchen müsste das zu
fixen.
Und hier scheint der Bug auch schonmal aufgefallen zu sein:
http://sourceforge.net/p/sdcc/mailman/message/29544602/
Nach endlosem Testen mit und ohne Klammern, mit "" und ohne "" ist mir
klar, dass da was nicht stimmt mit dem sdcc, allein schon die seltsamen
Fehlermeldungen oder "critical internal error"
Was mit einer Reihe Zeichenketten funktioniert ist:
wobei man die scharf begrenzen muss, ein [] funktioniert nicht, er muss
wissen wie lang der laengste String ist.
const char array[6][34] = {
("111111111111111111111111111111111"),
("222222222222222222222222222222222"),
("333333333333333333333333333333333"),
("444444444444444444444444444444444"),
("555555555555555555555555555555555"),
("666666666666666666666666666666666"),
};
printstrng(array[0]);
printstrng(array[1]);
ergibt die richtige lesbare Ausgabe
Christian J. schrieb:> 2Ar00r00 ist der Befehl ld hl,... und die 00r00 sind Platzhalter.
Vorsicht. Das ist ein anderer Befehl.
Der Z80 kann
1
LD HL, nn ; lade HL mit dem Wert nn
1
LD HL,(nn) ; lade HL mit dem Wert, den du unter der
2
; Adresse nn im Speicher findest
Der erste Befehl ist also der 'Immediate', während der zweite der
'indirect' Kategorie zuzuordnen ist.
Der SDCC benutzt da schon den richtigen.
Bei
1
0200 2Ar00r00 [16] 726 ld hl, (#_menue + 0)
holt er sich also den Inhalt des Speichers von hier
1
0000 829 __xinit__menue:
2
0000r61r02 830 .dw ___str_0
Unter Berücksichtigung der Relokierung durch den Linker, sollte nach dem
Befehl daher 0x0361 im Register HL stehen, was korrekt wäre.
> Kompiliere ich menue[1] steht da 2A 04 20, also ein Wort weiter.
Jup. Jetzt holt er sich den Wert von hier
1
0000 829 __xinit__menue:
2
0000r61r02 830 .dw ___str_0
3
0002r78r02 831 .dw ___str_1 ; <----------
4
0004r9Ar02 832 .dw ___str_2
5
0006rBBr02 833 .dw ___str_3
nach dem 'Load HL indirect' steht also 0378 im Registerpaar HL.
AUch das wieder korrekt die Startsdresse des 2.ten Strings im Speicher.
> Der Hex Dump zeigt hier:> 2A 02 20 = ld hl, #2002> ....> Bei 2002 liegt das RAm, ab 0x2000 ist es da.>> Area Addr Size Decimal Bytes
(Attributes)
> -------------------------------- ---- ---- ------- -----
------------
> _INITIALIZER 00002020 0000000E = 14. bytes
(REL,CON)
Das ist allerdings in der Tat interessant.
Was zeigt dir denn der Hexdump bei 0x2002 nachdem das Pgm geladen wurde
und ev, Hochfahrinitialisierungen gemacht wurden?
Hallo Karl-Heinz,
erstmal danke dass du da so tief mit reinkriechst und das ganz ohne
dafür bezhahlt zu werden.
Ich stelle nur fest, dass es nicht funktioniert, da ich hier die
Hardware habe und das ganze Drumherum aufgesetzt, so dass ich unter
Linux editiere, der Cubietruck den Code kompiliert, der direkt im
Brenner ist, so dass ich ganz fix von der Änderung zum Neubrand komme.
Mir ist soweit klar, dass eine Tabelle angelegt wird, eben diese _xinit,
wo Vektoren auf die Strings ablegt werden und dass eine indirekte
Adressierung nötig ist. Aber das erklärt noch nicht den Zahlendreher mit
den 0202 und 2020.
Tiefer will ich da nicht einsteigen, da ich einen workaroud habe, es sei
denn Du hast Lust den Asm Code mal durch einen Debugger zu jagen und zu
schauen was da wirklich passiert.
PS: Ich kann nur den Hexdump des Brenners aufrufen, der das ROM abdeckt,
alles hinter 0x1fff ist nicht zu sehen, da das schon das RAM ist. Und da
komme ich nicht dran, da ich erst den Monitor schreibe.
PS:
Aktuell habe ich auf jeden Fall eine lauffähige Umgebung um auch Int
Mode 2 usw auf einem Z80 Minimalsystem zu fahren und dafür habe ich Tage
gebraucht, da es nirgends im Netz darüber was gibt und der sdcc Int
Modes nicht unterstützt. Man muss da einiges selbst stricken. Falls das
von Interesse ist stelle ich den gesamten Projektcode, der aktuell aus
viel Testroutinen besteht gern hier rein. Irgendjemand wird sicher mal
wieder einen Z80 bauen und vor genau den gleichen Problemen stehen, die
ich gelöst habe.
Christian J. schrieb:> Mir ist soweit klar, dass eine Tabelle angelegt wird, eben diese _xinit,> wo Vektoren auf die Strings ablegt werden und dass eine indirekte> Adressierung nötig ist. Aber das erklärt noch nicht den Zahlendreher mit> den 0202 und 2020.
Der ist allerdings interessant.
Wenn das Bestand hat, dann könnte das aber eher ein Linker Fehler sein,
der die Bestandteile im Code dann an die richtige Stelle setzt.
Du könntest interessehalber mal probieren, ob Compiler und Linker die
Tabelle im ROM lassen, wenn du sie so anlegst
1
constchar*constmenu[]={
2
"String 1",
3
"String 2"
4
};
dadurch, dass jetzt das Pointer Array ebenfalls const ist (und nicht nur
die Texte), müsste das Pointer Array nicht im RAM allokiert werden und
damit dann auch nicht aus dem ROM initialisiert werden.
> PS: Ich kann nur den Hexdump des Brenners aufrufen, der das ROM abdeckt,
Das ist zu früh. Denn vor dem main läuft ja eine
Initialisierungsroutine, die die Initalwerte aus dem ROM ins RAM
kopiert. D.h. die müsste ablaufen, dann könnte man im Speicher
nachsehen, was bei 0x2002 tatsächlich im Speicher steht.
Karl Heinz schrieb:> Das ist zu früh. Denn vor dem main läuft ja eine> Initialisierungsroutine, die die Initalwerte aus dem ROM ins RAM> kopiert.
Nein, ich weiss dass sowas zb beim ARM gemacht wird im Startup Code.
Hier habe ich aber die volle Kontrolle über alles und den Startup selbst
geschrieben. Da wird nix kopiert. Was ich kompiliere steht im ROM drin.
Allerdings hat die area .initializier sicher was zu bedeuten und man
kann da auch selbst Code für schreiben, wenn man diese Area definiert.
Dein Vorschlag erzeugt:
Christian J. schrieb:> Karl Heinz schrieb:>> Das ist zu früh. Denn vor dem main läuft ja eine>> Initialisierungsroutine, die die Initalwerte aus dem ROM ins RAM>> kopiert.>> Nein, ich weiss dass sowas zb beim ARM gemacht wird im Startup Code.> Hier habe ich aber die volle Kontrolle über alles und den Startup selbst> geschrieben. Da wird nix kopiert.
Da muss aber was kopiert werden!
wenn du im C Code schreibst
1
intarr[]={1,2,3,4,5};
dann muss das Array im RAM angelegt werden (denn du könntest ja im
Programm dann eine Zuweisung an eines der Array Elemente machen, ergo
muss es änderbar sein) UND irgendwie müssen ja beim Hochfahren des
Systems die Initialwerte in diesen Speicher bugsiert werden. Die
Initialwerte müssen daher irgendwo im ROM stehen und beim Startup werden
sie ins RAM kopiert.
Wenn du das in deinem Startup nicht machst, ist aber klar dass aus dem
nicht ordnungsgemäss initialisiertem RAM nur Unsinn gelesen wird.
> Was ich kompiliere steht im ROM drin.
Auch.
> Dein Vorschlag erzeugt:> [code]> 740 ;main.c:95: printstrng(menu[0]);> 0209 2Ar6Br02 [16] 741 ld hl, (#_menu + 0)
Was wird da im Hex-Dump draus?
Ist die Adresse beim 2A dann eine ROM oder eine RAM Adresse. Sprich: Hat
der Compiler das const Array im ROM belassen, oder hat er dafür RAM
angefordert?
Christian J. schrieb:> Nachwort:>> Dein Vorschlag funktioniert !!!!!
Hätte ich dem SDCC jetzt gar nicht zugetraut, dass er das handhabt. :-)
>> Woran lag es jetzt? An dem extra "const" ?
Ja.
Denn jetzt ist auch das Array mit den Pointer-Werten const. Ergo ist es
nicht mehr veränderbar. Ergo braucht es kein RAM dafür und kann im ROM
verbleiben. Alle Lesezugriffe können daher direkt aus dem ROM
abgewickelt werden, wo der Compiler die Initialwerte angelegt hat.
Mir macht die Sache mit dem Initilizer noch Sorge, grad mal etwas
gebastelt:
Damit er nicht alles wegoptimiert ein volatile
1
volatileuint16_tn=0x1234;
2
volatileuint16_tmyvar=0xabcd;
3
4
n=5*i;
5
n=myvar;
Er macht es tatsächlich, vor Main fängt er globale Vars zu init'en:
1
743 ;main.c:90: volatile uint16_t n = 0x1234;
2
0210 DD 36 FE 34 [19] 744 ld -2 (ix),#0x34
3
0214 DD 36 FF 12 [19] 745 ld -1 (ix),#0x12
4
746 ;main.c:91: volatile uint16_t myvar = 0xabcd;
5
0218 21 CD AB [10] 747 ld hl,#0xABCD
6
021B E3 [19] 748 ex (sp), hl
Allerdings frage ich mich ob er diese .initializer Section noch sonstwie
braucht und ich da Code für schreiben muss.
Aus deinem Beispielarray macht er nix, legt nur eine Tabelle an. Aber
irgendwelche Zuweisungen stehen da nicht.
960 .area _INITIALIZER
0000 961 __xinit__arr:
0000 01 00 962 .dw #0x0001
0002 02 00 963 .dw #0x0002
0004 03 00 964 .dw #0x0003
0006 04 00 965 .dw #0x0004
0008 05 00 966 .dw #0x0005
967 .area _CABS (ABS)
Christian J. schrieb:> Er macht es tatsächlich, vor Main fängt er globale Vars zu init'en:>>
1
> 743 ;main.c:90: volatile uint16_t n = 0x1234;
2
> 0210 DD 36 FE 34 [19] 744 ld -2 (ix),#0x34
3
> 0214 DD 36 FF 12 [19] 745 ld -1 (ix),#0x12
4
> 746 ;main.c:91: volatile uint16_t myvar =
5
> 0xabcd;
6
> 0218 21 CD AB [10] 747 ld hl,#0xABCD
7
> 021B E3 [19] 748 ex (sp), hl
8
>
Interessant wie er das macht.
Die übliche Variante ist nicht, da extra Code für jede Variable zu
schreiben, sondern Compiler und Linker bauen da normalerweise im ROM im
Grunde genommen ein Speicherabbild zusammen, wie es eigentlich im RAM
vorliegen müsste und eine stinknormale Byteschleife kopiert dann einfach
alles ins RAM. Der Z80 kann das mit einem LDIR besonders gut.
Kann natürlich sein, dass das System das hier einzeln auf den Variablen
macht, weil so ein Speicherabbild zusammen mit der Kopierschleife mehr
Platz verbrauchen würde.
Leg doch mal ein Array mit 20 Einträgen an und sieh nach, wie sich der
Compiler vorstellt das zu initialisieren
1
volatileinta[20]={0,10,1,11,2,12,3,13,4,14,
2
5,15,6,16,7,17,8,18,9,19};
3
4
intmain()
5
{
6
}
irgendeine Form eines Initialisierungscodes muss es geben.
Christian J. schrieb:> Doch macht er in einer lokalen Funktion:
Da haben sie wohl davor zurückgeschreckt :-)
WOW. Was das an ROM kostet!
> Aber nicht wenn man es global definiert, dann gibts nur>
1
> 000A 972 __xinit__foom:
2
> 000A 01 00 973 .dw #0x0001
3
> 000C 02 00 974 .dw #0x0002
4
> 000E 03 00 975 .dw #0x0003
5
> 0010 04 00 976 .dw #0x0004
6
> 0012 05 00 977 .dw #0x0005
7
> 978 .area _CABS (ABS)
8
>
Hilft aber nichts. Irgendwo MUSS kopiert werden! Das RAM initialisiert
sich nicht von alleine auf die richtigen Werte.
Ich kriege nur grad die Krise:
Er soll sich ja das Programm Hex File vom PC reinziehen und das wird
dann auf 0x2000 lokiert sein, da das RAM da anfängt. Nach dem Reinladen
soll er dann auf 0x2000 springen und das RAM ausführen.
Hoffe mal da gibt es keine Probleme bei mit den ganzen Segmenten, Inits
usw.
>>Hilft aber nichts. Irgendwo MUSS kopiert werden! Das RAM initialisiert>>sich nicht von alleine auf die richtigen Werte.
Wie ?????? Ein Königreich dafür, wenn ich das wüsste, weil ich es
eigentlich gar nicht wissen muss, es soll nur funktionieren.
Christian J. schrieb:> Ich kriege nur grad die Krise:>> Er soll sich ja das Programm Hex File vom PC reinziehen und das wird> dann auf 0x2000 lokiert sein, da das RAM da anfängt. Nach dem Reinladen> soll er dann auf 0x2000 springen und das RAM ausführen.
?
Dein Speicherdump spricht da aber eine andere Sprache.
Der Code ist nicht auf 0x2000 relokiert.
> Wie ?????? Ein Königreich dafür, wenn ich das wüsste, weil ich es> eigentlich gar nicht wissen muss, es soll nur funktionieren.
Wie hast du denn den Rest des Startup Codes geschrieben?
Der Linker wird da ja Code einbinden, der vor main() abläuft. Da muss
diese Initialisierung irgendwo drinn stecken.
sieht vernünftig aus. Genau so etwas würde ich da erwarten.
Der LDIR kopiert BC Bytes von (HL) nach (DE).
In HL steht die Adresse vom Bytefeld, in dem alle Initialisierungen
zusammengetragen wurden (ist also eine Adresse im ROM), in DE steht die
Adresse vom RAM, wo sie hinmuessen.
logisch. Du initialisierst ja das RAM überhaupt nicht!
Bevor du nach _main weitergehst müssen doch erst mal die
Initialisierungen ins RAM kopiert werden. Das ist klar, dass da nichts
geht.
Ohne mich da jetzt näher damit befasst zu haben:
_GSINIT wird 2 mal angegeben? Da stimmt doch schon wieder was nicht.
Sieh dir mal das in deinem Link angegebene Startup File an. Ist zwar für
ein anderes System, aber die generelle Strategie kann man dort schon
sehen.
1
start_program:
2
3
;
4
; Vorgeplänkel wegen Interrupts. Wird dich weniger interessieren
5
;
6
im 1 ; interrupt mode -> rst 38h
7
di
8
9
;
10
; BSS ausnullen, damit wie vom C Standard vorgeschrieben alle globalen
11
; Variablen einen Wert von 0 haben
12
;
13
xor a ; clear carry
14
ld bc,#0x3b8 ; ram size left
15
ld hl,#0x7000 ; starting from 7000
16
ld de,#0x7001
17
ld (hl),a
18
ldir ; zero-fill bss
19
20
;
21
; dann die globalen Variablen initialisieren, die im C Code
22
; von 0 verschiedene Werte haben
23
;
24
call gsinit ; Initialize global variables.
25
26
;
27
; irgend eine systemspezifische Initialisierung. Offenbar irgendwas mit Sounds
28
; interessiert dich nicht und fliegt raus
29
;
30
ld h,#0 ; set dummy sound table
31
call set_snd_table
32
33
;
34
;
35
; random Generator.
36
; wenn mans braucht
37
;
38
ld hl,#0x0033 ; initialise random generator
39
ld (0x73c8),hl
40
41
;
42
; dann kommt irgendwas mit einem Display
43
;
44
; set screen mode 2 text
45
call 0x1f85 ; set default VDP regs 16K
46
ld de,#0x4000 ; clear VRAM
47
xor a
48
ld l,a
49
ld h,a
50
call 0x1f82
51
52
;
53
;
54
; und erst jetzt ist der Startup soweit fertig, dass main loslegen kann
Jetzt nicht mehr. Trotzdem führt der Sprung von call gsinit ins Ram.
Der wird als
CD 2A 20
übersetzt. Vermutlich weil diese Area da hingelegt wird. Leider weiss
ich nicht ob ich da was verändern kann, da das wohl Schlüsselworte sind,
die man nicht verändern sollte. Sonst würde ich gsinit zu meinem Code
nehmen vor dem Sprung ins hauptprogramm.
Du, Heinz, ich dank dir aber ich muss jetzt schlafen. Versuche das
morgen rauszufinden, ist ja schon eine Menge gewesen heute.
Auch andere haben das gleiche Problem... gähnhttp://comments.gmane.org/gmane.comp.compilers.sdcc.user/2405
Hi,
When I use my custom crt0, in the final binary, gsinit: is pointing to
GSFINAL instead of GSINIT. Therefore, global variables do not get
initialised. Also, GSINIT is placed into DATA which seems to be wrong.
Finally, since my device only has RAM and not ROM, I don't think I
need separate CODE and DATA areas - all variables can be stored in
CODE like consts are. Is there some way to tell the compiler to do
that?
Umschreiben der Kopierroutine in die .code area hat nix genutzt. Er wird
ausgeführt aber ein Test ergab dass eine 6 nicht auf dem Display kommt,
also wird sie nicht eingeschrieben.
Christian J. schrieb:> das passt auch.
Das kann aber nicht stimmen.
Die Routine kopiert x Bytes von der Adresse 0x2002 nach 0x2020. Auf
deinem System ist das aber, wenn ich mich recht erinnere RAM und nicht
ROM. D.h. die kopiert von uninitalisiertem RAM ins uninitialisierte RAM.
Die Initialiwerte müssen irgendwo in deiner ROM Sektion liegen. Denn nur
der wird ja von deinem Loader (Brenner?) beschrieben, wie du weiter oben
irgendwo erzählt hast (also von 0x0000 bis 0x1FFF).
Karl Heinz schrieb:> Christian J. schrieb:>> Doch macht er in einer lokalen Funktion:>> Da haben sie wohl davor zurückgeschreckt :-)>> WOW. Was das an ROM kostet!
Es war mal etwas effizienter, und wird es hoffentlich auch irgendwann
mal wieder sein:
https://sourceforge.net/p/sdcc/bugs/1565/
Philipp
Philipp Klaus Krause schrieb:> Karl Heinz schrieb:>> Christian J. schrieb:>>> Doch macht er in einer lokalen Funktion:>>>> Da haben sie wohl davor zurückgeschreckt :-)>>>> WOW. Was das an ROM kostet!>> Es war mal etwas effizienter, und wird es hoffentlich auch irgendwann> mal wieder sein:>> https://sourceforge.net/p/sdcc/bugs/1565/
Da du dich mit dem SDCC scheinbar auskennst, kannst du Christian mit
seinem Startup Code helfen?
Ich hab da nicht wirklich Ahnung davon, wie das funktioniert und muss da
mehr mit educated guesses arbeiten.
Christian J. schrieb:> push bc> 0212 CDr20r00 [17] 741 call _printstrng> 0215 F1 [10] 742 pop afKarl Heinz schrieb:>> So>> fit bin ich da auch´nicht drin. Vermutlich wird die Adresse in BC, DE>> übergeben an _printstrng.>> BC>> DE ist da nur Nebenschauplatz. Könnte damit zusammenhängen, dass> Calling-Konventions einzuhalten sind, die 2 Registerpärchen am Stack> vorschreiben.
Eher damit, dass in D oder E lokale Variablen gespeichert sind, die nach
dem Aufruf noch gebraucht werden (und die Aufrufkonvention _printstrng
erlaubt, D und E zu überschreiben).
Philipp
Zur Initialiserung von Variablen im Z80-port von sdcc (gilt auch für
z180, gbz80, r2k, r3ka, tlcs90):
Die wichtigsten areas sind:
1
.area _CODE
2
.area _INITIALIZER
3
.area _HOME
4
.area _GSINIT
5
.area _DATA
6
.area _INITIALIZED
_CODE: Enthält Code und konstante Variablen. Auf Systemen mit ROM wird
dieser Bereich im ROM angelegt.
_INITALIZER: Enthält ein Speicherabbild nichtkonstanter globaler
Variablen, die explizit initialisiert wurden. Dieses wird im ROM
angelegt, und muß in crt0 nach _INITIALIZED kopiert werden.
_GSINIT: Enthält code, der Variablen initialisiert, die nicht auf
anderem Weg intialisert werden. Muß0 von crt0m ausgeführt werden. Dieser
Bereich liegt auch im ROM.
_DATA: Hier liegen im RAM die Variablen die entweder nicht explizit
initialisiert werden (und dann von crt0 mit 0 initialisiert werden
müssen) sowie die Variablen, die in _GSINIT initalisert werden.
_INITIALIZED: Hier liegen im RAM die explizit initialisierten Variablen.
Philipp
; Ordering of segments for the linker - copied from sdcc crt0.s
31
.area _CODE
32
.area _INITIALIZER
33
.area _HOME
34
.area _GSINIT
35
.area _GSFINAL
36
.area _DATA
37
.area _INITIALIZED
38
.area _BSEG
39
.area _BSS
40
.area _HEAP
41
42
.area _CODE
43
44
_cv_start:
45
ld sp, #0x7400 ; Set stack pointer directly above top of memory.
46
47
; Implicitly zeroed global and static variables. Do it here, before any call to not corrupt stack!
48
ld bc, #1023
49
ld hl, #0x7000
50
ld (hl), #0x00
51
ld e, l
52
ld d, h
53
inc de
54
ldir
55
56
call gsinit ; Initialize global and static variables.
57
58
call _cv_init ; Initialize Colecovision specific stuff.
59
call _main
60
rst 0x0 ; Restart when main() returns.
61
62
vint:
63
push af
64
push bc
65
push de
66
push hl
67
push iy
68
call _cv_vint
69
pop iy
70
pop hl
71
pop de
72
pop bc
73
pop af
74
retn
75
76
spint:
77
push af
78
push bc
79
push de
80
push hl
81
push iy
82
in a, (#0xfc)
83
ld b, a
84
in a, (#0xff)
85
ld c, a
86
push bc
87
ld hl, (#_cv_spint_handler)
88
call __sdcc_call_hl
89
pop af
90
pop iy
91
pop hl
92
pop de
93
pop bc
94
ei
95
reti
96
97
.area _GSINIT
98
gsinit::
99
100
; Explicitly initialized global variables.
101
ld bc, #l__INITIALIZER
102
ld a, b
103
or a, c
104
jr Z, gsinit_static
105
ld de, #s__INITIALIZED
106
ld hl, #s__INITIALIZER
107
ldir
108
109
gsinit_static:
110
; Explicitly initialized static variables inserted by compiler here.
111
112
.area _GSFINAL
113
ret
114
115
.area _HOME
Wenn ich mir das durchlese, sehe ich, dass ich manches da inzwischen
schöner schreiben könnte. Aber verwednen wir trotzdem 'mal dieses
Beispiel zur weiteren Erläuterung.
Philipp
Am Anfang gibt es ein paar Bytes, deren Bedeutung vom BIOS des
ColecoVision bestimmt wird. Insbesondere sind dort Adressen für
Interruptroutinen.
Beim Einschalten des ColecoVision übergibt das BIOS irgendwann an
_cv_start.
Dort wird dann zuwerst der stackpointer initialisert.
Dann wird der RAM mit 0 initialisiert (ginge etwas effizienter, da es
ausreichend wäre nur _DATA mit 0 zu initialisieren).
Dann geht es weiter bei _gsinit. Dort wird dann _INITIALIZER nach
_INITIALIZED kopiert. Die Symbole l__X und s__X werden für jeden Bereich
X vom Linker durch die Größe des Bereichs und die Startadresse des
Bereichs ersetzt.
Philipp
Hallo,
danke für die Ausführungen. Ich werde mal versuchen das hinzukriegen,
sobald ich vom Zahnarzt wiederkommen, falls ich dann noch den Nerv dazu
habe :-)
Der SDCC ist soweit klasse aber die Doku ist etwas zu wenig. Bei mir
funktioniert das Variaben kopieren immer noch nicht und ich weiss nicht
mehr was ich noch wohin schieben oder kopieren muss. Ich habe wohl alles
ausgereizt. So ein Schaubild würde echt helfen, wie da was angeordnet
wird im Speicher, der ja nun mal sehr einfach ist bei Z80.
Das ganze soll klar strukturiert sein und vor allem müssen Defintionen
klar von Code unterschieden werden können. Warum taucht 2 Mal .gsinit
auf?
Da warst du schneller :-) Ich konnte es nicht mehr korrigiere. Leider
zeigt der Call ins RAM, wie ich im Hex File sehe. UNd da liegt natürlich
kein Code :-( Das Programm schmiert direkt ab.
CD 18 20 wird aus call gsinit. also call 2018.
Wie kriege ich gsinit dazu, dass es in der Zeropage liegt? Da wo es hin
gehört?
;; Start des Hauptprogramms nach der Zeropage
.org 0x80
init:
ld sp,#stack ;; Stack an Spitze des RAM legen
; Ram ausnullen
xor a ; clear a and carry
ld bc,#0xdfff ; ram size left
ld hl,#0x2000 ; starting from 2000
ld de,#0x2001
ld (hl),a ; 0 -> (2000)
ldir ; (HL) -> (DE)
call gsinit
jp _main ; ###### Hauptprogramm aufrufen #######
endlos:
jr endlos ; Sicher ist sicher
;; Ordering of segments for the linker.
.area _CODE
.area _INITIALIZER
.area _HOME
.area _GSINIT
.area _GSFINAL
.area _DATA
.area _INITIALIZED
.area _BSEG
.area _BSS
.area _GSINIT
gsinit::
; Explicitly initialized global variables.
ld bc, #l__INITIALIZER
ld a, b
or a, c
jr Z, gsinit_static
ld de, #s__INITIALIZED
ld hl, #s__INITIALIZER
ldir
gsinit_static:
; Explicitly initialized static variables inserted by compiler here.
.area _GSFINAL
ret
.area _HOME
>> Philipp
Das Gleiche, die Reihenfolge sollte ja egal sein.
PS: Wenn das erfolgreich alles klappt schreibe ich nen schönen Wiki
Artikel über das Thema.
Philipp Klaus Krause schrieb:> Zur Initialiserung von Variablen im Z80-port von sdcc (gilt auch für> z180, gbz80, r2k, r3ka, tlcs90):>> Die wichtigsten areas sind:>>
1
> .area _CODE
2
> .area _INITIALIZER
3
> .area _HOME
4
> .area _GSINIT
5
> .area _DATA
6
> .area _INITIALIZED
7
>
>> _CODE: Enthält Code und konstante Variablen. Auf Systemen mit ROM wird> dieser Bereich im ROM angelegt.> _INITALIZER: Enthält ein Speicherabbild nichtkonstanter globaler> Variablen, die explizit initialisiert wurden. Dieses wird im ROM> angelegt, und muß in crt0 nach _INITIALIZED kopiert werden.> _GSINIT: Enthält code, der Variablen initialisiert, die nicht auf> anderem Weg intialisert werden. Muß0 von crt0m ausgeführt werden. Dieser> Bereich liegt auch im ROM.> _DATA: Hier liegen im RAM die Variablen die entweder nicht explizit> initialisiert werden (und dann von crt0 mit 0 initialisiert werden> müssen) sowie die Variablen, die in _GSINIT initalisert werden.> _INITIALIZED: Hier liegen im RAM die explizit initialisierten Variablen.>> Philipp
Ich knüpfe hier nochmal an, da es wirklich nirgendwo Infos darüber gibt,
was das alles zu bedeuten hat und weil ich mein Projekt nicht deswegen
aufgeben werde.
.area _CODE
.area _INITIALIZER
.area _HOME
.area _GSINIT
.area _GSFINAL
.area _DATA
.area _INITIALIZED
.area _BSEG
.area _BSS
.area _HOME
Da würde noch ein Stack Segment fehlen, wie zb beim 486er.
Ein Z80 Minisystem besteht aus ROM und RAM. Code wird aus dem Rom heraus
ausgeführt und dieser Code erzeugt Variablen etc im Ram. Im Ram liegt
grundsätzlich kein Code. Gewisse Variablen haben Startwerte, sie liegen
alle hintereinander im Ram und könnten wie ein Image aus dem ROM
bespielt werden.
Es fehlt jede Info darüber wie der sdcc diese "Segmente" verteilt,
klatscht er sie einfach hintereinander? Oder teilt er sie in Gruppen
auf? Was hat die Reihenfolge zu bedeuten? Und wo müssen diese Inits im
crt0.s stehen? Mittendrin? Unten? Darf man sie doppelt definieren?
Einen Teil ins ROM, den anderen ins Ram? HEAP kann ja nur im Ram liegen.
.area _GSFINAL
Das sagt mir soweit nichts.
Wie gesagt zeigt bei mir der Vektor er GSINIT ins RAm, weil dort eine
Routine vermutet wird, die nicht da ist. Packe ich den Init Code ins Rom
klappt es trotzdem nicht.
Ich bin da mit meinem Latein am Ende :-(
Hier nochmal mein batch Compile File
# Assembliere das crt0 startup file
sdasz80 -o -l crt0.rel crt0.s
# Kompilieren aber nicht linken
echo "Kompilieren....."
sdcc -mz80 main.c -c
#sdcc -mz80 tools.c -c
# Nun alles Linken
echo "Linking....."
sdcc -mz80 --no-std-crt0 --vc --code-loc 0x0100 --data-loc 0x2000
crt0.rel main.rel
Hallo,
falls hier noch jemand mal reinschaut, so habe ich kapiert was falsch
läuft indem ich ein Minimalprogramm aufsetzte, so dass alles Unötige weg
war.
Da liegt der Wurm drin !!!!
Mich wunderte es dass die Intialisierten Variablen nirgendwo auftauchten
im brennbare Hex Dump. Klar, denn sie wurden weit außerhalb des ROM
gelegt. Daher lief auch die Kopierroutine nichtl, denn die kopierte
Nichts nach Nichts.
Übeltäter:
--data-loc 0x300 im Bash File zum Kompilieren
Testweise mal auf 0x300 gesetzt, damit er meint das Ram liege dort,
obwohl es ROM ist. Und oh Wunder, da lagen die Variablen dann auch artig
hintereinander.
Was da eigentlich kopiert wird entzieht sich meinem Verständnis
Der Compiler erzeugt den Code so, dass die intialisierten Variablen auch
direkt da im Ram zu liegen kommen, wo sie hin sollen. Meine 0x2000 lagen
weit außerhalb des 8kb ROMs, daher verschwanden sie einfach.
Leider kann man ihm nicht beibringen wo denn die _INITIALIZER Area sein
soll.
Damit dürfte dieses Problem als nicht lösbar abgeklärt sein.
Eigentlich sollte sdcc, wenn crt0.rel die erste aufgeführte Objektdatei
ist, die areas so wie in crt0 aufgelistet anordnen, also _INITIALIZER
direkt nach _CODE.
Es gibt aber auch die Linker-Option -b um manuell festzulegen, welche
area wohin kommt.
Philipp
Eine Linker Option -b ist nicht dokumentiert. Und die Reihenfolge darf
keine Rolle spielen. Ein Vertauschen ändert auch den Namen der .hex
Datei, hat aber keinen Einfluss auf das Ergebnis.
Glaube du hast mich nicht richtig verstanden. Der sdcc macht das richtig
aber er legt die Init Werte schon sofort und direkt ins Ram ab, da wo
--data-loc hinzeigt.
Christian J. schrieb:> Eine Linker Option -b ist nicht dokumentiert.
Man muß dazu sdld selbst aufrufen, statt das sdcc zu überlassen. Zur
Dokumentation:
1
>sdld
2
3
sdld Linker V03.00 + NoICE + sdld
4
5
Usage: [-Options] [-Option with arg] file
6
Usage: [-Options] [-Option with arg] outfile file1 [file2 ...]
7
Startup:
8
-p Echo commands to stdout (default)
9
-n No echo of commands to stdout
10
Alternates to Command Line Input:
11
-c ASlink >> prompt input
12
-f file[.lk] Command File input
13
Libraries:
14
-k Library path specification, one per -k
15
-l Library file specification, one per -l
16
Relocation:
17
-b area base address = expression
18
-g global symbol = expression
19
Map format:
20
-m Map output generated as (out)file[.map]
21
-w Wide listing format for map file
22
-x Hexadecimal (default)
23
-d Decimal
24
-q Octal
25
Output:
26
-i Intel Hex as (out)file[.ihx]
27
-s Motorola S Record as (out)file[.s19]
28
-j NoICE Debug output as (out)file[.noi]
29
-y SDCDB Debug output as (out)file[.cdb]
30
List:
31
-u Update listing file(s) with link data as file(s)[.rst]
Leider kann ich mit
area base address = expression
nichts anfangen und alle möglichen Sytaxversuche sind auch gescheitert.
man sdld und man sdcc bringen auch keine Klarheit.
Ich lege die Sache daher beiseite, da ich darauf schon zuviel Zeit
verschwendet habe und ich mich eigentlich um sowas auch nicht kümmern
möchte, wenn ich einen Compiler benutze. Es ist zwar Open Source und
eine freiwillige Arbeit aber die ist leider schlecht dokumentiert.
Es erzeugt darüber hinaus unterschiedlichen Code, ob man erst beide
Teile kompiliert und dann linkt, oder ob man das in einem Rutsch macht.
Dann sind die Konstanten nämlich doppelt im Speicher vorhanden, hinter
dem Code, wo sie hingehören sollten und im RAM auch noch was ja nicht
gebrannt werden kann. Ein
char array[10000]
bläst das Hex File auf über 10000 Bytes so auf, dass man es nicht mehr
brennen kann, weil es ein direktes Speicherabbild ist. Auch das ist in
dem obigen Link beschrieben.
Der SDCC erwartet, dass ein "Image" in ein Z80 Memory aus reinem RAM
geschoben wird. Dann ist es nämlich richtig. Mit ROM und RAM zusammen
kommt er nicht klar.
Ok, abgehakt.... ich schaue mich nach was anderem um als dem sdcc. Auch
wenn ich dafür was bezahlen muss. Oder vermeide initilisierte Variablen
und schreibe erst im Code das rein was rein muss.
Kannst Du uns 'mal genau sagen, was Dein Problem ist?
Also:
Bis zu 20 Zeilen kompilierbarer C-Code, zusammen mit Deiner crt0.s.
Mit der Kommandozeile, die Du verwendet. Und mit Angaben, an welchen
Adressen Deines Zielsystems RAM bzw. ROM liegen.
Philipp
Ich sehe gerade, dass Du Angaben zum System in einer Mail gemacht hast
("System 0-1ffff ROM, 2000-ffff RAM."), und die crt0.s läßt sich
möglicherweise auch zusammen suchen. Ich mache 'mal ein BEispiel für
Dein System.
Philipp
Christian J. schrieb:> Der SDCC erwartet, dass ein "Image" in ein Z80 Memory aus reinem RAM> geschoben wird.
Nein.
> Dann ist es nämlich richtig. Mit ROM und RAM zusammen> kommt er nicht klar.
Doch.
Philipp
Hallo Phillip,
anbei mal ein "20 Zeilen" projekt, direkt kompilierbar mit compile.sh
Ich habe alles weggelöscht was zuviel ist und es auf das Wesentlich
rediziert. Vieleicht noch etwas mit den Zahlen spielen in der compile.sh
Gleich noch mehr dazu....
PS: Ich sehe grad dass die .org Statements im crts0.s entfernt sind. Die
erzeugen Assemblerfehler, zumindest an bestimmten Stellen. Ich wollte
den Startupcode abgesetzt in der Zeropage und das Userprogramm ab 0x100
liegen haben.
Bin dabei .....
Christian J. schrieb:> anbei mal ein "20 Zeilen" projekt
Da seehe ich im .map schon mal, dass ein paar areas an der falschen
Stelle liegen. Ich schaue 'mal nach, was der Grund dafür ist.
Philipp
Christian J. schrieb:> anbei mal ein "20 Zeilen" projekt, direkt kompilierbar mit compile.sh> Ich habe alles weggelöscht was zuviel ist und es auf das Wesentlich> rediziert. Vieleicht noch etwas mit den Zahlen spielen in der compile.sh
Zwei kleine Änderungen an Deinem Projekt sind nötig, damit es klappt:
1) Die Reihenfolge der areas in Deiner crt0.s:
1
;; Ordering of segments for the linker.
2
.area _CODE
3
.area _HEADER
4
.area _HEADER0
5
.area _HEADER1
6
.area _HEADER2
7
.area _HEADER3
8
.area _HEADER4
9
.area _HOME
10
.area _INITIAL
11
.area _INITILIZER
12
.area _GSINIT
13
.area _GSFINAL
14
.area _DATA
15
.area _BSEG
16
.area _BSS
17
.area _HEAP
_INITIALIZED muß aber im RAM, also nach _DATA kommen.
Richtig wäre z.B.:
Der RAM liegt aber, soweit ich weiß, an 0x3000, nicht 0x300. Und
crt0.rel muß zuerst kommen, damit der Linker die Reihenfolge der areas
von dort übernimmt.
Richtig wäre z.B.:
So,
ich bin durch. Es funktioniert nicht. Ich habe dazu das ganze
Testprojekt genommen, da ich nur da die Ausgabe auf einer Anzeige habe,
die mir die Realtität wiedergibt. Die crts0 habe ich soweit veinfacht
wie es geht, keinen Call mehr zum gsinit usw. So weit es geht jede
Fehlerquelle rausgenommen.
Auffällig ist, dass
.globl l__INITIALIZER
.globl s__INITIALIZER
.globl s__INITIALIZED
extra angegeben werden müssen in crt0.s. Ich dachte die seien
vordefiniert und daher bekannt. Das sind bekanntlich Schlüsselworte.
Es kann direkt mit ./compile.sh fehlerfrei durchkompiliert werden und
erzeugt main.ihx als Ausgabe.
Es kann alles ignoriert werden (dient nur der Hardware Initilisierung)
außer der crt0.s und im Bereich main.c der letzte Teil
1
char abc = 5;
2
3
int main() {
4
5
InitHardware();
6
SetDigit(abc);
7
while(1);
8
return(0);
9
}
Würde abc wirklich mit 5 initialisiert, dann stünde auf der 7-Segment
Anzeige eine 5 beim Starten. Es steht aber eine 0 dort, weil ich das RAM
ausnulle. Nulle ich es nicht, sondern "viere" es steht da eine 4 drin.
Ändert man den Code ab
SetDigit(5);
wird richtig eine 5 angezeigt.
Im Hex Dump Editor sehe ich die Strings und anderes hinter dem
Codesegment, wo sie wohl auch hingehören
Fazit:
Der Kopiervorgang der Initwerte findet nicht statt.
Ich würde Dir einen Orden umhängen, wenn das Problem endlich gelköst
wäre!
Gruss,
Christian
Moment..... da warst Du schon schneller. Ich muss das jetzt erstmal
durchtesten. Kann ich Dich ggf mal anrufen? Bist Du aus Deutschland oder
Schweiz oder Österreich?
So, fertig. Ich habe alles so gemacht wie beschrieben. Das ERgebnis ist
"0" auf der Anzeige und nicht "5". Anbei alles wie gerade vor 5 Minuten
durchkompiliert.
ROM: 0x0000 - 0x1fff
RAM: 0x2000 - 0xffff
Jetzt weiss ich nicht mehr weiter :-((((
Gruss,
Christian
Und nochwas, siehe Bild:
Das ist das Eprom Brennprogram mit dem Hex Dump. Setzt man --data-loc
auf 0x500 testweise, so liegt das RAM ja im "sichtbaren" Bereich für das
Brennprogram, was genau 8kb abbildet, die Größe des EPROM.
Und wie man sieht liegen da jetzt vorbelegte Strings von RAM Variablen:
char array[]="Hallo Welt! Hier bin ich!";
Und die sind doppelt vorhanden, hinter dem Code und dann nochmal im
"Pseudo-Ram" ab 0x500. (siehe anderes Bild)
Ich habe es auch ausprobiert erst zu kompilieren und dann zu linken in
einem zweiten Schritt. Gleiches Ergebnis
sdcc -mz80 main.c -c
sdcc -mz80 --code-loc 0x100 --data-loc 0x200 --no-std-crt0 crt0.rel
main.rel
Ich habe mir jezt grösste Mühe gegeben das zu beschreiben, hoffe es
hilft.
Es läuft !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
Und zwar nur mit genau dieser Anordung und Konstellation in der crt0.s
Das Dumme ist nur, ich weiss nicht genau warum.....
Christian J. schrieb:> So, fertig. Ich habe alles so gemacht wie beschrieben. Das ERgebnis ist> "0" auf der Anzeige und nicht "5". Anbei alles wie gerade vor 5 Minuten> durchkompiliert.>> ROM: 0x0000 - 0x1fff> RAM: 0x2000 - 0xffff>> Jetzt weiss ich nicht mehr weiter :-((((>> Gruss,> Christian
Wenn ich getrennt kompiliere, also Deine compile.sh z.B. zu
Du brauchst dann, später noch den Sprung nach gsinit, da leider die
Initalisierung mit _INITIALIZER / _INITALIZED zur Zeit nur für globale
Variablen funktioniert. Der _GSINIT-Mechanismus wird noch für lokale
Variablen mit storage class static gebraucht.
Philipp
Philipp Klaus Krause schrieb:> Du brauchst dann, später noch den Sprung nach gsinit, da leider die> Initalisierung mit _INITIALIZER / _INITALIZED zur Zeit nur für globale> Variablen funktioniert. Der _GSINIT-Mechanismus wird noch für lokale> Variablen mit storage class static gebraucht.>> Philipp
Hi,
zumindest klappt es jetzt, dass die globalen Vars richtig eingestellt
werden, das habe ich grad auch mit dem Menue getestet was ich von
const char* const menu...
auf
const char* menue
änderte.
Denn Sprung nach gsinit? Ich habe doch alles sprunglos gemacht. Ich
verwende in Interrupts static char Variablen, die aber alle mit 0
eingestellt sind. Wären es 5 gäbe es da Probleme? Weil ausgenullt ist ja
alles schon.
Damit kann ich leben wenn static nicht läuft, da kann man auch
workarounds programmieren.
Ich fasse, da Du ja zum Team von sdcc gehörst mal ein paar Fragen
zusammen, damit ich und vielleicht auch andere es verstehen, wie läuft.
Eine Ergänzung der Doku wäre auch ganz wichtig was den Z80 angeht, damit
man das wirklich verinnerlicht.
1. Stört die Verwendung von .org 0xxxx im crt0.s File irgendwie?
Der Assembler meckert dann. Der Startup Code wird jetzt direkt vor
das Userprogramm geklebt. Ich hätte ihn gern in der Zeropage gehabt.
2. Ist es irgendwie wichtig an welcher Stelle diese .area Defintionen
im crt0.s File stehen? Vorne, hinten, mittig? Biher merke ich nur,
dass man danach kein .org mehr setzen darf, wie zb vorher bei den Ints.
3. Das sieht seltsam aus, es ist von oben kopiert. Muss in die _GSFINAL
noch irgendwas rein?
; Explicitly initialized global variables.
ld bc, #l__INITIALIZER
ld a, b
or a, c
jr Z, gsinit_static
ld de, #s__INITIALIZED
ld hl, #s__INITIALIZER
ldir
gsinit_static:
; Explicitly initialized static variables inserted by compiler here.
.area _GSFINAL
ret
.area _HOME
Christian J. schrieb:> Ich fasse, da Du ja zum Team von sdcc gehörst mal ein paar Fragen> zusammen, damit ich und vielleicht auch andere es verstehen, wie läuft.> Eine Ergänzung der Doku wäre auch ganz wichtig was den Z80 angeht, damit> man das wirklich verinnerlicht.
Allerding befasse ich mich kaum mit Assembler oder Linker, sondern
hauptsächlich mit manchen der Backends im Compiler.
Philipp
Christian J. schrieb:> 1. Stört die Verwendung von .org 0xxxx im crt0.s File irgendwie?> Der Assembler meckert dann. Der Startup Code wird jetzt direkt vor> das Userprogramm geklebt. Ich hätte ihn gern in der Zeropage gehabt.
Es gibt verschiedene Arten von areas: ABS und REL. Erstere werden von
Hand platziert, letztere vom Linker. .org sollte nur in ersteren
vorkommen, d.h.
1
.area _TEST1(ABS)
2
.org 23
ist ok, aber
[code
.area _TEST2
.org 42
[/code]
nicht.
Philipp
Christian J. schrieb:> 3. […] Muss in die _GSFINAL> noch irgendwas rein?
Soweit ich weiß, ist _GSFINAL nur für das ret da:
Der Compiler generiert Code zur Initalisierung von Variaben (lokale
static) in _GSINIT. Der Linker setzt alles dasin _GSINIT nacheinander,
und da er weiß, dass danach _GSFINAL kommt, steht dann das ret an der
richtigen Stelle.
Philipp
Christian J. schrieb:> 2. Ist es irgendwie wichtig an welcher Stelle diese .area Defintionen> im crt0.s File stehen? Vorne, hinten, mittig? Biher merke ich nur,> dass man danach kein .org mehr setzen darf, wie zb vorher bei den Ints.
Entscheidend ist, in welcher Reihenfolge der Linker die areas zum ersten
Mal sieht. Denn in dieser Reihenfolge versucht er, sie anzuordnen
(soweit man ihm nicht per -b etwas anderes sagt). Es wäre also schlecht,
wenn eine .area, die in der Liste vorkommt auch vor der Liste schonmal
vorkommt. Ein weiteres Vorkommen nach der Liste schadet dann nicht mehr.
Philipp
Hi,
hauptsache ist, dass es jetzt funktioniert und mein kleines Maschinchen
mit dem hohen Stromverbrauch sinnvolle Sachen macht! Auf die static
verzichte ich, das geht auch anders zb als globale Variablen.
Ich danke Dir ganz herzlich für Deine Geduld und dass ich es jetzt
verstanden habe. Habe den Startup jetzt wieder bei 0x80 liegen in dem
ich die areas hinten an gestellt habe, ganz zum Schluss bis auf Header.
Und so ist es auch logisch und verständlich.
Die Frage ob das alles auch noch klappt, wenn ich künftig das Programm
vom PC über die Uart durch den Monitor als Hex oder Bin einlade und es
dann direkt aus dem Ram starte kommt später, denn dann wird der
Startupcode ja zweimal ausgeführt, da sich das im RAM liegende Programm
ja auch initialisieren muss. Wobei es dann auch egal ist ob die
Einstellung des Monitors im Eprom überschrieben werden, sie werden nicht
mehr gebraucht.
Aber das kommt später....
Grüße,
Christian
Christian J. schrieb:> ; Ram ausnullen> xor a ; clear a and carry> ld bc,#0xdfff ; ram size left> ld hl,#0x2000 ; starting from 2000> ld de,#0x2001> ld (hl),a ; 0 -> (2000)> ldir ; (HL) -> (DE)
Hallo Christian,
Dein Code ist nicht wirklich schön, da nicht portabel.
Ich habe Deinen Code mal etwas aufgehübscht: