ich versuche mich gerade an PIC Assembler, einfach weil ich es interessant finde. Dazu habe ich einen PIC12F508 zur Verfügung, also ein Winzling unter den PICs, aber übersichtlich vom Befehlssatz her :-)
Nun brauche ich eine Variable im RAM für einen Zähler (Loop) und da stehe ich etwas auf dem Schlauch. Laut Datenblatt hat der PIC12F508 25 Bytes RAM (uh, yeah!) und wenn ich es richtig verstanden habe werden diese 25 Bytes sowohl für die Register der MCU als auch für das Programm benutzt.
Würde das heißen das die erste verfügbare RAM-Adresse dann 07h wäre? Also z.B. so um diese zu nutzen:
Danke das mit der Variablen und der Schleife habe ich bereits hinbekommen, ich wollte nochmal sicher gehen das ich das alles so richtig verstanden habe :-)
Jetzt hänge ich am CALL/RETLW, der will noch nicht. Ich habe eine Schleife programmiert die eine bestimmte Anzahl von 1/0-Übergängen am GP2 auslöst.
1
count EQU 0x07;
2
3
...
4
movlw 16;
5
movwf count;
6
call send;
7
...
8
stop:
9
goto stop;
10
11
send:
12
bcf GPIO, 2; // LED ON (low)
13
bsf GPIO, 2; // LED OFF (high)
14
nop;
15
nop;
16
nop;
17
decfsz count, 1;
18
goto send;
19
retlw 0;
Da passiert leider nix, so als würde die Subroutine nicht angesprungen werden. Ich habe es versucht mit dem Simulator nachzustellen, aber die Subroutine wird nicht angesprungen.
Im Simulator sehe ich das meine Variable an RAM Position 07h ordentlich befüllt wird. Danach kommt der CALL und dem wird mit F7 (Step Into) nicht mehr gefolgt.
In der "SFRs" Anzeige im Simulator sehe ich das nach Ausführung des CALL die richtige Nachfolge-Adresse auf den HW-Stack gelegt wird. Jedoch wird der der PCL (Program Counter) auf die Adresse 0xBC eingestellt. Mein Programm fängt aber erst ab 0xE9 an. Die richtige Adresse wäre 0xF5. Warum biegt der CALL denn hier falsch ab?
In der "output" Ansicht wird das sogar angezeigt:
"No source code lines were found at current PC 0x1bc. Use Program memory view to see instruction code disassembly"
In der "SFRs" Anzeige im Simulator sehe ich das nach Ausführung des CALL
die richtige Nachfolge-Adresse auf den HW-Stack gelegt wird. Jedoch wird
der der PCL (Program Counter) auf die Adresse 0xBC eingestellt. Mein
Programm fängt aber erst ab 0xE9 an. Die richtige Adresse wäre 0xF5.
Warum biegt der CALL denn hier falsch ab?
Hast Du "4.7 Program Counter" im Datenblatt gelesen, speziell was beim CALL passiert? Vielleicht erklärt es das ja.
bcf GPIO, 2; // LED ON (low)
bsf GPIO, 2; // LED OFF (high)
Diese zwei Befehle passieren im Mikrosekunden Bereich, so schnell, dass man gar nichts sieht.
Du musst zwischen diesen zwei noch eine Verzögerungsschleife einbauen.
Hast Du "4.7 Program Counter" im Datenblatt gelesen, speziell was beim
CALL passiert? Vielleicht erklärt es das ja.
Ja, habe ich, aber es hat mich nicht weiter gebracht. Ich manipuliere den PC nicht, PA0 ist 0 und der 12F508 hat ja nur eine Page. Das OSCCAL wird ordentlich beladen und auch sonst kann ich jeden Schritt im Simulator nachvollziehen, nur eben den CALL nicht.
Der PC ist ja praktisch nur 8-Bit breit. Er könnte also eine Adresse wie 0x1F5 garnicht aufnehmen. Im Assembly-Code steht auch der Mnemonic "09 F5", also ist es innerhalb der Bank, die ja nur 256 12-Bit Instruktionen enthalten kann, eine art relativer Call.
Der Compiler scheint die Programme immer ans Ende vom Flash zu legen...
Ich muss mich hier nochmal einlesen wo mein Denkfehler liegt mit dem PC:
"Because PC<8> is cleared in the CALL instruction or any modify PCL instruction, all subroutine calls or computed jumps are limited to the first 256 locations of any program memory page (512 words long)."
Ohne das ich mir das Gemurkse jetzt angesehen haette, 2 Tipps:
Benutze mindestens einen PIC, den dein Programmieradapter auch
debuggen kann. Z.B. einen 16F819 o.ae. Meinen ersten PIC habe
ich z.B. von einem HP-Batterypack fuer den RAID-Controller recycled.
Das war/ist besagter 16(L)F819...
Und wirf blos das Winzzwergel 508 in eine d0nkle Ecke.
Um die "Familie" kennenzulernen, ist der schlicht ungeeignet.
Lass den Einfachst-Assembler links liegen, und installiere dir
XC-8 und benutze den dort mitgelieferten Assembler.
Fuer das alte MP-Lab ist die letzte verwendbare XC-8 Version: 1.45.
Spaetestens, wenn einem die kleinen Kerlchen naeher gekommen sind,
wird man auch C benutzen wollen, und das auch gerne mit ein wenig
Assembler aufpeppen wollen. Da ist man dann, wenn man den XC-8
Assembler bereits kennt, klar im Vorteil.
Ich habe den Eindruck ich bin mal wieder Opfer von zu neuer Technik geworden. Das MPLAB-X scheint hier die alten 8-Bitter nicht mehr ordentlich zu unterstützen, bzw. gibt es massig Berichte auf Stackoverflow darüber.
Es ist wohl so das der Linker das Programm immer am oberen Ende der Bank ausrichtet, ein "ORG" bringt da nichts.
Und wenn ich den Satz zum CALL aus dem Datenblatt richtig verstanden habe können CALL Zieladressen nur innerhalb der ersten 100h Bytes einer Bank liegen.
Mein Versuch den Code nach "unten" im Adressbereich zu bekommen sind bislang alle gescheitert.
1
PSECT resetVec, class=CODE, delta=2
2
resetVec:
3
goto main
4
5
PSECT myMain, class=CODE, delta=2
6
main:
7
...
Eingestellt mit Linker-Option "-PmyMain=0020h"
Ich habe mal den map-output in den Linker-Options aktiviert und da erkennt man das er nichtmal den Reset-Vector richtig platziert:
1
Non line specific message::: warning: (2092) relocatable list file is not available; fixup overflow list file messages cannot be generated
Dazu habe ich einen PIC12F508 zur Verfügung, also ein
Winzling unter den PICs, aber übersichtlich vom Befehlssatz her :-)
Für "übersichtlich" kannst Du Dir nichts kaufen. Mehr Befehle sind immer besser, Du mußt ja nicht gleich alle benutzen.
"übersichtlich" übersetze mit "stark eingeschränkt", das kommt eher hin.
Mit nur 2 Level Stack und ohne Interrupts möchte ich nie arbeiten müssen. Da muß man zu sehr um die Ecke denken, um selbst einfachste Projekte zu realisieren.
Ich hab mich früher immer gewundert, warum PIC-Programme immer soviel gleichen Code hintereinander enthalten. Bei dem winzigen Stack sind Calls das pure Gift, daher wurde das Unterteilen in Unterfunktionen weitgehend vermieden. Was aber stark zu Lasten der Lesbarkeit und Erweiterbarkeit geht.
Ich hab mit 8051 angefangen, da war Stack nie eine Problem und daher habe ich auch fleißig gecallt und Unterfunktionen benutzt.
Ich würde Anfängern nicht unter ATtiny85 empfehlen, da paßt selbst float in C drauf, wenn man möchte. Und der hat auch nur 8 Pins.
Wird im MPLAB mit der Voreinstellung "relocatable" oder "absolute" gearbeitet?
Mit "absolute" sollte der Code dort geladen werden wo man es selbst bestimmt.
Nachtrag:
Habe selbst etliches mit den sehr ähnlichen PICs aus der 10F200 -Serie gemacht. (12-Bit Befehlswort; gleicher Befehlssatz, noch weniger Pins und weniger Speicher)
Mit "absolute"-Voreinstellung f. die Codegenerierung hatte ich nie solche Probleme.
wird ordentlich beladen und auch sonst kann ich jeden Schritt im
Simulator nachvollziehen, nur eben den CALL nicht.
Meine Erfahrung ist auch, dass man sich nicht zu 100% auf den Simulator verlassen kann. Übrigens kann man sich auch nicht zu 100% auf den Codeconfigurator von MPLAX verlassen.
Und siehe da, plötzlich funktionieren auch die CALLs wie erwartet. Es scheint also wirklich so zu sein das der kleine Baseline PIC nur Zieladressen in den ersten 100h Adressen der Bank anspringen kann.
Ja, so nach und nach komme ich dahinter das die 12F Serie eher nur für einfachste Steueraufgaben zu gebrauchen ist...
P.S.: MPASM gibt es mit der neuen X-IDE nicht mehr, der wurde durch XC8 ersetzt.
Meine machen aber was sie sollen. Und ich muss da auch
keinen anderen fragen.
Die Einstellung "absolut" und "relocatable" gibt es schon
im uralten MPLAB und bezog sich auf den MPASM.
Da du ja aber immer nur "Neuling" bist, sehe ich dir diese
Unkenntnis gerne nach.
Du hast zwar jetzt "einen" Weg gefunden, der ist dir aber
verbaut, wenn du noch andere Dinge dazulinken willst.
Deswegen koennte man jetzt den von dir berschrittenen Weg
auch als "falsch" bezeichnen.
Es
scheint also wirklich so zu sein das der kleine Baseline PIC nur
Zieladressen in den ersten 100h Adressen der Bank anspringen kann.
Steht exakt so im Datenblatt, Bit A8 = 0:
"For a CALL instruction, or any instruction where the PCL is the destination, bits 7:0 of the PC again are provided by the instruction word. However, PC<8> does not come from the instruction word, but is always cleared (Figure 4-6)."
Für einen Call in die 2. Pages muß man also ein Label in den 1. Pages callen, wo dann ein Goto zum Ziel steht.
Ja, so nach und nach komme ich dahinter das die 12F Serie eher nur für
einfachste Steueraufgaben zu gebrauchen ist...
So ist es, du hast dir den schlimmsten Krüppel unter den Pics ausgelesen. Die neueren 12F sind extrem besser, haben auch Interrupt und interessante Peripherie.
Deswegen koennte man jetzt den von dir berschrittenen Weg
auch als "falsch" bezeichnen.
+1
Anstelle von Rumgemurkse mit "absolut" und ORG und EQU und ...
sollte man versuchen zu lernen, wie man das im aktuellen
Jahrtausend dem Linker überlässt.
MPLAB_XC8_PIC_Assembler_User_Guide_for_Embedded_Engineers.pdf
könnte dabei hilfreich sein. -> 9. Baseline Code Example
Ist leider ohne ganz ausfühliche Erläuterungen, aber man kann evtl.
auch mal da schauen -> 4. A Basic Example For Mid-range Devices
Selber Linker spielen macht nur für spezielle use cases Sinn, also <org> wird besser immer durch psect Anweisung ersetzt. Wenn es sein muss, werden die psect dann mit Linker Option auf absolute Addr plaziert innerhalb der Project Config.
Ich habe hierzu nur einen use case, dass ich am flash ende eine Prüfsumme plaziere. Sonst brauche ich sowas nie.
Steht da nicht der Kalibrierwert fuer den internen Oszillator?
Den braucht man natuerlich nicht, wenn der PIC seinen Takt von
einem Quarz(-ozillator) bezieht. Aber sonst?
Man kann auch Datentabellen in die erste Page legen.
Dann braucht man fuer den Zugriff das High-Register nicht
und kann es einfach loeschen statt einen Wert setzen zu muessen. :)
Also habe ich der PSECT ein "abs" angefügt:PSECT resetVec, class=CODE,
delta=2, abs
...
und schon klappt es mit dem "ORG" unterhalb eine absolute Adresse
vorzugeben:
Es könnte sein, dass da nicht immer das dabei raus kommt, was man denkt
User Guide
4.9.41 Org Directive
The ORG directive changes the value of the location counter within the
current psect. This means that the addresses set with ORG are relative to
the base address of the psect, which is not determined until link time.
Note: The much-abused ORG directive does not move the location counter to
the absolute address you specify.
So ist es, du hast dir den schlimmsten Krüppel unter den Pics
ausgelesen. Die neueren 12F sind extrem besser, haben auch Interrupt und
interessante Peripherie.
Die neuen 12F können deutliche mehr, werden aber für Anfänger schnell unübersichtlich, so hat der PIC12F1840 schon 2 Pages und die Register sind auf 8 Banks verteilt.
werden aber für Anfänger schnell
unübersichtlich, so hat der PIC12F1840 schon 2 Pages und die Register
sind auf 8 Banks verteilt.
Insbesondere für Programmierer, die aus der 8051- oder AVR-Ecke kommen, ist das ganze Banking- und Paging-Geraffel eine Qual. Das sollte man besser dem C-Compiler überlassen.
PIC in Assembler ist wirklich nur was für eingefleischte Fans, die vorher nicht von anderen Architekturen ohne diesen Hassle verwöhnt worden sind.
Insbesondere, wenn durch Änderungen sich Code über Pagegrenzen verschiebt, sind Anfänger regelmäßig geschockt, wenn der PIC plötzlich Amok läuft (z.B. RETLW-Fallgrube).
Sehr schön einfach finde ich den 8051-Assembler. Mit den Bitbefehlen kann man direkt Logik-Gatter implementieren, mit MOVC bequem Tabellenzugriffe, mit DPTR computed Jumps und Calls und 8Bit-MUL/DIV sind auch ganz nett.
Ja, ich habe es verstanden, vielen Dank. Dennoch interessant das alles mal zu hören und zu erfahren, auch wenn es nur dazu dient zu wissen was man besser nicht macht ;-)