Forum: Mikrocontroller und Digitale Elektronik 8052 keil assembler JZ $+4


von Entwickler (Gast)


Lesenswert?

Hallo Leute,
1
t1:
2
    mov     a,e_timer
3
    jz      $+4   
4
    dec     e_timer
5
    mov     b_clk, #50d

wird bei der Berechnung der Sprungadresse $+4 für $ die Adresse von jz 
genommen oder die nachfolgende?

Ich habe im Datenblatt nachgeschaut und sehe, dass sowohl Opcodes für 
"jz rel" wie auch für "dec direct_byte" 2 Bytes beanspruchen. Also 4 
zusammen.

Wenn nun $+4 auf die Adresse von "mov" zeigt, dann ist mir der Code 
klar.
Aber ich finde die Berechnungsweise von $+x nirgendwo. Sicher weiß 
jemand aus dem Forum das und kann das bestätigen oder Link zur 
Beschreibung geben :)

Gruß und Danke

von (prx) A. K. (prx)


Lesenswert?

Üblicherweise beziehen sich solche Angaben in Assembler auf die Adresse 
des Befehls selbst, nicht auf die möglicherweise anders arbeitende 
Berechnungsweise des Befehls.

von Thomas Z. (usbman)


Lesenswert?

das kannst du auch selbst probieren:
Label: sjmp label ;und
       sjmp $
erzeugen den gleichen code. bedeutet das $ ist ein Platzhalter (label) 
auf den Anfang des Befehls. $+4 zeigt 4 Bytes weiter.

Ich würde nur in Ausnahmen mit dem $ Platzhalter arbeiten. Besser ist es 
Label zu verwenden. Mit dem $ kann es schnell Unfälle geben wenn man am 
Code was ändert.
https://www.keil.com/support/man/docs/a51/a51_wp_locationcounter.htm

: Bearbeitet durch User
von m.n. (Gast)


Lesenswert?

(prx) A. K. schrieb:
> Üblicherweise beziehen sich solche Angaben in Assembler auf die Adresse
> des Befehls selbst, nicht auf die möglicherweise anders arbeitende
> Berechnungsweise des Befehls.

War es vielleicht beim 6502? Wenn ich mich richtig erinnere, gab es auch 
CPUs, die den Offset zum nächsten Befehl verwendet haben, da bei 
Ausführung des rel. Sprunges der PC schon auf den nächsten Befehl 
zeigte.
Oder H8, H8S, H8SX? Zu lange her.

von Cartman (Gast)


Lesenswert?

Kann der Keilassembler denn keine lokalen Sprungmarken,
dass man mit dem "$" herummurksen muss?

Frueher oder spaeter faellt man mit dem naemlich doch
mal auf die Nase...

von Thomas Z. (usbman)


Lesenswert?

Cartman schrieb:
> Kann der Keilassembler denn keine lokalen Sprungmarken,
> dass man mit dem "$" herummurksen muss?

nein lokale Labels kenne ich nur von Assembler Dialekten die sowas wie 
PROC/ENDP unterstützen also zB Turbo Assembler auf dem PC. Da kann man 
dann Labels generieren die nur innerhalb der Proc gültig sind.

Beim Keil A51 sind Labels auf Dateiebene global. Er kennt allerdings 
lokale Labels in Macros.

von (prx) A. K. (prx)


Lesenswert?

m.n. schrieb:
> War es vielleicht beim 6502? Wenn ich mich richtig erinnere, gab es auch
> CPUs, die den Offset zum nächsten Befehl verwendet haben, da bei
> Ausführung des rel. Sprunges der PC schon auf den nächsten Befehl
> zeigte.

Das ist häufig so, hat aber keinen Einfluss auf die Arbeitsweise eines 
symbolischen Assemblers. Der berechnet erst $+4 als Zieladresse, 
basierend auf der Befehlsadresse, denn das ist zu diesem Zeitpunkt die 
einzige, die er kennt. Bei der Codeerzeugung subtrahiert er dann jene 
Adresse, auf die sich der Maschinenbefehl bezieht.

von (prx) A. K. (prx)


Lesenswert?

Cartman schrieb:
> Kann der Keilassembler denn keine lokalen Sprungmarken,
> dass man mit dem "$" herummurksen muss?

Kurze Suche im Web ergab nur welche bei Keil für ARM.

von m.n. (Gast)


Lesenswert?

Ich habe mal in alten .lst-Dateien gesucht und folgende Stelle gefunden:
1
00E0 6002          JZ      $ + 4H
Der Offset von +4 wird also als +2 kodiert.

von Mikroschnarpel (Gast)


Lesenswert?

Spaetestens wenn der Assembler noch Befehle einfuegt
oder die Reihenfolge der Befehle aendert, ist beim
"$" Schluss mit lustig.

Beispiele:
TMS320 Assembler nimmt Aenderungen vor, um Silicon Bugs
zu umgehen.
MIPS Assembler aendert je nach eingestelltem Modus die
Reihenfolge der Befehle um Zyklen zu sparen.
XC8 PIC Assembler "streut" Befehle ein, um das
Bankregister/bits zu setzen.

Beim 8052 koennte ich mir erweiterte Architekturen
vorstellen, die manche Befehle mit "Praefixen" kennzeichnen
muessen, die dann so auch nicht im Assemblerquelltext
stehen.

Wie man sieht, betritt man mit dem "$" eher ein Minenfeld...

Schon der blosse Wechsel des Assemblers kann unerwartete
Ergebnisse hervorbringen.

von Patrick L. (Firma: S-C-I DATA GbR) (pali64)


Lesenswert?

Mikroschnarpel schrieb:
> Wie man sieht, betritt man mit dem "$" eher ein Minenfeld...

Ist ein Guter Rat :-)

Obwohl die Meisten Assembler die ich kenne und das sind nicht wenige, 
interpretieren das $ als die Adresse in welcher der Befehl steht.
Beispiel im MSP430 Assembler:
1
      BR $         ; Neverending loop
wird gerne in den Applikation Examples von TI gebraucht um den Prozessor 
"Festzufahren",um beispielsweise den Watch Dog zu prüfen, oder bei 
kleinen Applikation Examples auf ein Interrupt zu warten ohne den 
Prozessor schlafen zu legen.

Man kann $ Perimeter auch brauchen um etwa ein Link auf den Befehl zu 
speichern ist aber echt nicht zu empfehlen sobald man "$+x" verwendet.
Auch der IAR kann im Assembler Befehle einfügen um Siliconbugs zu 
umgehen!
Dann fällt man so richtig schön auf die Nase und sucht eine halbe 
Ewigkeit den Fehler ;-)

73 55

von (prx) A. K. (prx)


Lesenswert?

Bei solcher Schreibweise könnte man irgendwann drüber stolpern, dass 
Atmels Assembler wie der Prozessor Worte zählt, der aus dem Binutils 
aber Bytes.

von Martin H. (horo)


Lesenswert?

m.n. schrieb:
> War es vielleicht beim 6502? Wenn ich mich richtig erinnere, gab es auch
> CPUs, die den Offset zum nächsten Befehl verwendet haben, da bei
> Ausführung des rel. Sprunges der PC schon auf den nächsten Befehl
> zeigte.
> Oder H8, H8S, H8SX? Zu lange her.

Bei der Z80 ist die relative Endlosschleife 18 FE (JR -2), d.h. der PC 
zeigt bei der Ausführung schon auf den nächsten Befehl.

von m.n. (Gast)


Lesenswert?

Bevor man heute herummeckert, muß man beachten, daß der Keil C-Compiler 
aus dem Anfang der 80er stammt. In dessen Listings ist dann auch die 
Form $+4H zu finden und auch nur für diese kurzen Sprünge. Weitergehende 
Verzweigungen oder auch das Überspringen von INC wurden alle mit Labels 
in der Form ?Cxxxx versehen.

Seinerzeit hatten Rechner wenig Speicher, die Editoren waren rudimentär, 
Assembler erlaubten Label mit 6 - 8 Zeichen Länge und waren ziemlich 
lahm. Für einen Programmierer war es daher angenehm eindeutige 
Sprungziele mit $+x abzukürzen, ohne wieder ein neues Label zu 
verwenden.
Ein Kollege hatte einen Assembler (von Intel oder Philips), der selbst 
bei kleinen Programmen auf einem IBM-PC Minuten für die abschließende 
.hex-Datei brauchte.
Kompatibel zu anderen CPUs mußte man nicht sein - die übrigen 
Unterschiede waren eh zu groß.

von 888 (Gast)


Lesenswert?

Martin H. schrieb:

> Bei der Z80 ist die relative Endlosschleife 18 FE (JR -2), d.h. der PC
> zeigt bei der Ausführung schon auf den nächsten Befehl.

Um den Maschinencode "18 FE" für "JR -2" zu erzeugen, schreibt man im 
Assembler-Quellcode "JR $".

$ ist auf die Adresse des aktuell übersetzten Befehls, also die 
Speicherzelle in der 0FEh für "JR" steht. Die "-2" im Befehl bezieht 
sich auf den Program Counter in der CPU, der bei Ausführung des 
JR-Befehls bereits auf das erste Byte des folgenden Befehles zeigt.

von Peter D. (peda)


Lesenswert?

$ ist einfach nur ein spezielles Label für die Adresse des aktuellen 
Befehls. Was daraus dann für ein Code generiert wird, darum kümmert sich 
der Assembler.
Du kannst z.B. auch schreiben "LJMP $", dann enthält der Code die 
aktuelle Adresse.

Besonders beim 8051 ist, daß er schon im Voraus das nächste Byte liest, 
auch wenn es nicht ausgeführt wird. Hast Du z.B. an 0x1000 ein RET 
stehen, holt er noch den Befehl von 0x1001 und springt dann an die 
Adresse aus dem Stack zurück.

von (prx) A. K. (prx)


Lesenswert?

Peter D. schrieb:
> Besonders beim 8051 ist, daß er schon im Voraus das nächste Byte liest,
> auch wenn es nicht ausgeführt wird.

Diese Methode ist eigentlich recht verbreitet. Auch die AVR Cores halten 
das so, und schon der 6502.

von Nachdenklicher (Gast)


Lesenswert?

Praktisch (aber bisher von mir nicht beobachtet) wäre auch ein 
Platzhalter für "nächster Befehl". Folgendes Konstrukt habe ich in 
8051-Code schon häufig gesehen:

CJNE $REG #$VAL next_instruction
next_instruction:
J[N]C somewhere

Genutzt wird das als einfacher größer als/kleiner als-Vergleich. Dabei 
ist es völlig egal, ob der Sprung ausgeführt wird oder nicht, es wird 
sich nur zunutze gemacht, daß die Auswertung das Carryflag setzt/löscht, 
es wird so oder so das J[N]C ausgeführt.

Klar, man könnte
CJNE $REG #$VAL $+3 schreiben, aber wehe, man vertippt sich da mal, und 
schreibt aus Versehen +2 oder +4...

von Thomas Z. (usbman)


Lesenswert?

Nachdenklicher schrieb:
> Praktisch (aber bisher von mir nicht beobachtet) wäre auch ein
> Platzhalter für "nächster Befehl".

sowas lässt sich ganz einfach mit einem Macro bauen. Die Macro 
Funktionen beim Keil A51 sind ziemlich mächtig.

von Nachdenklicher (Gast)


Lesenswert?

Thomas Z. schrieb:
> sowas lässt sich ganz einfach mit einem Macro bauen. Die Macro
> Funktionen beim Keil A51 sind ziemlich mächtig.

Den hab ich nicht. ;-) Weiß nicht mal, ob es den für Linux gibt.
Aber der, den ich mir gerade selbst schreibe, wird so etwas haben. Auch 
lokale Labels und so Späße. Wird nur aufgrund von begrenzter Zeit noch 
ein Weilchen dauern. (Und ja, ich habe mit 8051 noch genug zu tun, daß 
sich das für mich lohnt, neue Firm- für alte Hardware und so...)

von Thomas Z. (usbman)


Lesenswert?

Du schreibst in der heutigen Zeit noch einen Assembler für den 51er?

Du kannst da natürlich Features reinpacken wie du willst, ich gebe aber 
zu bedenken dass die Quellen dann nur zu deinem Assembler passen. Ob das 
so eine gute Idee ist?

von Nachdenklicher (Gast)


Lesenswert?

Thomas Z. schrieb:
> Du schreibst in der heutigen Zeit noch einen Assembler für den 51er?

Ja. Wie gesagt, ich arbeite damit im Sinne von "neue Firmware für alte 
Hardware" noch recht viel. Und die Auswahl von guten 8051-Assemblern 
(Betonung auf gut) für Linux ist doch eher... meh. Ist alles ein recht 
spezieller Anwendungsfall, den zu erklären hier zu lange dauern würde.
Trust me, I know what I am doing. ;-)
Wir reden hier allerdings auch nicht von kommerziell und Broterwerb, 
sondern von Feierabendbastelei. Deshalb steht Keil da auch nicht zur 
Debatte, selbst wenn es den für Linux geben sollte, weil teuer. Beim 
Brotwerwerb steht der Fokus auf ARM.

Thomas Z. schrieb:
> Du kannst da natürlich Features reinpacken wie du willst, ich gebe aber
> zu bedenken dass die Quellen dann nur zu deinem Assembler passen. Ob das
> so eine gute Idee ist?

Ist an der Stelle egal, da der auch nur den Kram assemblieren muß, den 
ich ihm vorsetze. Und das ist in der Regel reverse engineered, und im 
Falle von in C geschriebenen Originalquellen auch oft so, daß die 
meisten Disassembler streiken*, weshalb ich das dann doch überwiegend 
manuell mache. Die Sorucen selbst verlassen meinen PC auch nur in Form 
eines verschlüsselten Cloud-Backups, da wird also auch nie jemand 
anderes dran gehen (und wenn doch, kriegt derjenige eben den Assembler 
dazu).

*) C51 hat seine case-Anweisung beispielsweise so gestrickt, daß direkt 
nach dem Funktionsaufruf die Sprungtabelle folgt, und der Rücksprung 
dann in der aufgerufenen Funktion berechnet wird (Adresse der 
Sprungtabelle liegt ja dann praktischerweise auf dem Stack). Da kommen 
die Disassembler, die ich bisher probiert habe, nicht mit klar. 
Insbesondere wenn die Interpretation der Sprungtabelle als Maschinencode 
mit dem tatsächlichen Ende überlappt, und die ersten 1-2 Bytes des 
folgenden Maschinencodes dann noch als Teil der disassemblierten 
Sprungtabelle interpretiert werden, und dadurch echte Instruktionen 
"verschwinden".

von Thomas Z. (usbman)


Lesenswert?

Nachdenklicher schrieb:
> C51 hat seine case-Anweisung beispielsweise so gestrickt, daß direkt
> nach dem Funktionsaufruf die Sprungtabelle folgt

ja das macht er aber nur wenn die case zusammenliegen zb 0..9.
die Libfunktion dazu ist dann ccase. In anderen fällen wird ein binärer 
Baum geprüft.
Wenn man die ccase lib funktion gefunden hat ist die Disassemblierung 
einfach.
braucht aber etwas Handarbeit. Ich arbeite oft mit IDA Pro und hab mir 
Flirt Tabellen zu identifikation der Keil libs gebaut.
https://github.com/usbman01/KeilC51flirt

Ich kenn unter linux nur den Assembler aus dem SDCC Paket und ja der ist 
nicht so toll.

von Nachdenklicher (Gast)


Lesenswert?

Thomas Z. schrieb:
> ja das macht er aber nur wenn die case zusammenliegen zb 0..9.
> die Libfunktion dazu ist dann ccase. In anderen fällen wird ein binärer
> Baum geprüft.

In Binaries, die ich in den Fingern hatten, waren das teilweise auch 
Werte, die völlig durcheinander waren. Hängt vielleicht auch von der 
Version ab?
Rein vom Ablauf her wäre es auch egal, da es immer Tupel 
Wert/Sprungadresse sind, und er linear die Liste von oben nach unten 
abarbeitet, bis entweder was passendes gefunden ist, oder nach einem 
0x0000-Trenner die "Default"-Sprungadresse kommt. Da ich leider nichts 
außer dem EPROM-Dump habe, weiß ich natürlich nicht, welche Version von 
C51 verwendet wurde. Aber die bekannten Bibliotheksfunktionen und 
Startup-Code sind ein sicheres Indiz, daß es C51 war. Allerdings wohl 
mit sehr seltsamer Konfiguration, da ausnahmslos alle Daten immer im 
XRAM lagen, abgesehen von Registern und Stack, die ja immer im IRAM 
sind. Auch der Optimizer war wohl nicht besonders gut, an etlichen 
Stellen wurde ein Wert erst ins XRAM gespeichert und sofort wieder aus 
diesem geladen. Funktioniert, ist aber nicht besonders performant. 
Insbesondere, wenn nichts dazwischenfunken und den Wert in A ändern 
kann. ;-)

Thomas Z. schrieb:
> Ich arbeite oft mit IDA Pro

Ist mir für Feierabendbasteleien zu teuer. ;-) Hatte mal Ghidra 
probiert, aber da kam nichts wirklich sinnvoll nutzbares bei raus.

von Thomas Z. (usbman)


Lesenswert?

Nachdenklicher schrieb:
> Allerdings wohl
> mit sehr seltsamer Konfiguration, da ausnahmslos alle Daten immer im
> XRAM lagen

wenn das so ist wurde im Large Mode übersetzt. Für mich eindeutig ein 
Zeichen dass derjenige keine Ahnung vom Keil hatte oder im das Ergebnis 
egal war.
Man kann auch im small Mode problemlos Variablen im XDATA plazieren.
Der Keil Optimizer ist immer noch der Beste den ich bisher für x51 
gesehen habe. Da kommt nach meinen Erfahrungen weder IAR noch SDCC ran.

Die Lib Funktionen sind eigentlich seit v3 weitgehend gleich geblieben. 
Der Compiler selbst wurde sicher seit der Umstellung auf 32Bit nicht 
mehr angefasst. Was sich später noch geändert hat waren spezielle 
Features bei bei bestimmten Targets. Das sind dann in der Regel 
spezialisierte Libs

Das ist aber hier jetzt OT

von Peter D. (peda)


Lesenswert?

Thomas Z. schrieb:
> wenn das so ist wurde im Large Mode übersetzt.

Ja, das Konzept des 8051 mit den verschiedenen Speicherbereichen haben 
viele nicht verstanden. Die Idee war schon recht clever, bis zu 128 Byte 
direct data schnell und mit kurzem Code zugreifen zu können. Ich habe in 
keinem Projekt die 64kB Flash ausfüllen können. Banking habe ich also 
nie benutzen müssen. Schade, daß die Typen mit 8kB SRAM (P89C668, 
AT89C51RE2) nicht mehr hergestellt werden.

von Peter D. (peda)


Lesenswert?

Nachdenklicher schrieb:
> *) C51 hat seine case-Anweisung beispielsweise so gestrickt, daß direkt
> nach dem Funktionsaufruf die Sprungtabelle folgt

Das hatte ich zu Assemblerzeiten auch sehr oft so gemacht. Dadurch wird 
der Code deutlich kompakter, da ich vor dem Aufruf keine Register laden 
muß oder den Stack. Auch ist es besser lesbar, wenn ein konstanter Text 
direkt nach dem Aufruf steht und nicht ganz woanders in einem 
Datensegment.

von (prx) A. K. (prx)


Lesenswert?

Peter D. schrieb:
> Die Idee war schon recht clever, bis zu 128 Byte
> direct data schnell und mit kurzem Code zugreifen zu können.

Dieser Teil war sehr clever. Die zweite Hälfte zu den 256 Bytes auch 
noch. Das war damals recht viel für einen Mikrocontroller. Mehr war 
eigentlich nicht vorgesehen gewesen. Man baute damals nicht fürs 
Jahrhundert, sondern auf kurze Sicht.

Jenseits von 256 Bytes liess die Cleverness dann etwas nach, weil die 
nervenden Kunden unbedingt einen 8051 mit einigen kB RAM haben wollten, 
statt des doch viel besseren 8096.

: Bearbeitet durch User
von Peter D. (peda)


Lesenswert?

Intel (80C251) und Philips (80C51XA) hatten mal versucht, einen zum 8051 
kompatiblen 16-Bitter rauszubringen. Haben aber beide keine Verbreitung 
gefunden.
Ich hab noch einen 80C251 samt Debugger über den LPT-Anschluß rumliegen. 
Beides aber nie benutzt.
Der Keil C251 Dongle könnte auch noch rumliegen. Nur habe ich keinen 
LPT-Port, um ihn anzuschließen.

von m.n. (Gast)


Lesenswert?

Peter D. schrieb:
> Intel (80C251) und Philips (80C51XA) hatten mal versucht, einen zum 8051
> kompatiblen 16-Bitter rauszubringen.

Als Nachfolger für den 8051 (oder genauer gesagt 80C552) hatte ich den 
SAB80C166 im Auge, was sich dann aber zerschlagen hat.

> Der Keil C251 Dongle könnte auch noch rumliegen. Nur habe ich keinen
> LPT-Port, um ihn anzuschließen.

Bei der DOS-Version mußte man nach einem bestimmten Muster im Programm 
suchen und einen Befehl durch NOP ersetzen. Dann hatte sich die Dongelei 
erübrigt ;-)

von W.S. (Gast)


Lesenswert?

m.n. schrieb:
> Der Offset von +4 wird also als +2 kodiert.

Ähem... nö.
Der Assembler weiß, daß der PC bereits auf den nächsten Befehl gestellt 
ist und daß der Relativsprung bei diesem Chip 2 Bytes lang ist. Folglich 
zieht er von dem gewünschten Offset 2 ab. So kommt dann diese 2 dabei 
heraus.

So machen es relativ viele Architekturen. Putzig war es nur bei den 
älteren ARM-Architekturen, wo man noch nicht sowas wie "RETURN"oder 
"RETI" hatte und stattdessen sowas schreiben mußte "SUBS PC,LR,#4".

Dennoch ist es allemal besser mit benamten Sprungzielen anstelle 
numerischer Offsets zu arbeiten. Egal ob da nun  der Assembler lokale 
Marken kennt oder nicht.

W.S.

von m.n. (Gast)


Lesenswert?

W.S. schrieb:
> Folglich
> zieht er von dem gewünschten Offset 2 ab. So kommt dann diese 2 dabei
> heraus.

Wieder einmal muß ich mich über Dich wundern. Nichts anderes habe ich 
geschrieben.

von W.S. (Gast)


Lesenswert?

m.n. schrieb:
> Wieder einmal muß ich mich über Dich wundern.

Und so wundern wir uns gegenseitig über uns.

Aber dein
m.n. schrieb:
> Der Offset von +4 wird also als +2 kodiert.

ist schlichtweg falsch, denn es gibt keinen "Offset von +4". Richtig 
wäre "der Offset von +2 wird also als +2 kodiert". Toll!

Was du meinst, ist wohl das Wissen der Hersteller des Assemblers, daß 
dieses $ nicht den Stand des PC zum tatsächlichen Ausführungszeitpunkt 
darstellt, sondern den Beginn des Befehls.

Ach, wir haben heuer neuere Kamellen. Laß die alten ruhen.

W.S.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.