Bascom und Assembler...

LotadaC

Sehr aktives Mitglied
22. Jan. 2009
3.547
70
48
Marwitz
Sprachen
  1. BascomAVR
  2. Assembler
...was ist alles zu beachten?

zu den Gründen muß ich wohl nicht viel sagen - gerade bei kurzen und zeitkritischen Sachen möchte ich bei Assembler bleiben - beim rumprobieren mal so auf die Schnelle, und bei komplizierten Algorithmen ist's mit Bascom einfacher. Außerdem scheinen einige speziellere Hardwarefunktionen mit den Bascom-Config-Befehlen nicht vorgesehen zu sein. Falls es bereits so eine Übersicht gibt, verweist mich bitte dahin (ich hab bisher nichts komplettes gefunden). Folgendes hab ich bisher zusammengetragen (bitte bestätigen/korrigieren/ergänzen):

-Bascom erkennt (und übersetzt/verwendet) die Assembler-Mnemonics, außer...
-gleichnamige Bascom-Mnemonics haben vorrang
-will man trotzdem das entsprechende Assembler-Mnemonic verwenden, muß man direkt ein "!" voranstellen.
-die Compiler-Direktiven "$asm" und "end asm" umschließen einen reinen Assembler-Codeblock
-Bascom pusht (Eintritt) und popt (Austritt) bei IRQs alle "Rechenregister" es sei denn, man legt die ISR mit einem nachgestellten "nosafe" fest.
-für jede Operation (also eine Zeile Bascom-Code) holt Bascom etwaige Variablen etc. aus dem Speicher (in Rechenregister), verrechnet diese, und schreibt das/die Ergebnis/se ggf zurück - insbesondere hier könnte man mit Assemblerverwendung sparen. (Stimmt das so?) Die verwendeten Register sollten also nach der Bascom-Code-Zeile wieder frei sein.
-Da eine Bascom-Zeile (i.a.) in mehrere Assembler-Befehle übersetzt wird, müssen in ISRs verwendete Register (ggf auch das SREG) auf dem Stack gesichert werden (aber halt nicht unbedingt alle;) )
-Bascom belegt außerdem "permanent" die Register R4/R5 (->FrameStack), R8/R9 (->Read(?)), das low-Nibble von R6. (noch mehr?).

Jetzt wirds noch ein wenig unsicherer:
-wie komme ich mit Assembler an, von Bascom definierte, Variablen ran? Einerseits kann man die Variable mit "DIM name AS typ AT adresse" definieren - die Adresse (konstant) kann dann auch in Assemblerinstruktionen verwendet werden. Was ist mit "LOADADDR var,Platzhalter"? Wenn ich das recht verstanden habe, existiert der Platzhalter nur für den Compiler, und wird beim assemblieren halt durch die Adresse (von var) ersetzt - die LOADADDR-Instruktion selbst erzeugt letztendlich keinen Code. (?)
-wie verhält sich das mit Sprüngen - also kann ich in einem ASM-Block ein Sprungziel festlegen, und aus einem anderen dahin jmpen? (überprüft Bascom insbesondere die zulässige Sprungdistanz?)
-wird irgendwie sichergestellt, daß bascom-generierte Sprungziele mit meinen nicht ... äh ... gleiche Namen haben?
-zumindest beim Mega88 gibt es 3(?) I/O-Register, die sich mMn noch mit IN/OUT ansprechen lassen - also "schneller" SRAM. verwendet Bascom sowas, oder hab ich da dann alleinigen Zugriff drauf? Achso, dabei fällt mir grad ein:
-kann man irgendwie Rechenregister "reservieren", sodaß Bascom die nicht überschreiben kann (für oft/zeitkritisch verwendete Konstanten/Variablen...)?

so, das wär erstmal alles, was mir bisher so auf die Schnelle dazu eingefallen ist. Dank an alle, die bis hier gelesen haben. Und jetzt bitte Korrekturen/Ergänzungen und (!) Bestätigungen.
 
mit Querverweis zu einem Themenverwandten Thread ...

Hallo,

das ist doch schon ne schöne Zusammenstellung ;)
Ich verlinke dann mal schnell zu nem ähnlichen Thread ...
BASCOM Library mit ISR und Variablen
eventuell sind da noch Infos die dir weiterhelfen. Ich muß mich da auch noch
irgendwann mal mit beschäftigen :D ... na mal sehen wann ...

Auf jeden Fall bläht BASCOM ein einfaches Programm in ziemlich viel Code auf.
Das habe ich bei meinem TWI/I2C-Analyzer (siehe Projektbereich) gemerkt.
Da ich da mit Assembler in der Sackgasse steckte wollte ich mit Bascom mal
nen schnellen Schritt nach vorne machen. Da hat das kleine Programm aber
nur dicke Backen gemacht. Mit der Flash-Füllung die Bascom bis jetzt hat
könnte ich in Assembler nicht nur den TWI-Analyzer programmieren sondern
auch den ganzen Rest. :rolleyes:

Ach ja ... ich hab mal mit nosafe versucht Code zu sparen und bin dabei
ziemlich gegen die Wand gerannt. Bei Interruptroutinen geht dann garnix
mehr. Das Programm holpert nur noch vor sich hin, die LCD-Ausgabe wird
zerstückelt und was weiß ich alles. Also Resourcenschonend würde ich
BASCOM nicht unbedingt nennen. Leider. Alleine aus dem Grund möchte
ich auch auf kurz oder lang wieder größere Teile in Assembler machen.

Gruß
Dino
 
Hallo LotadaC :cool: ,

da hast du dich schon einmal ziemlich in die Assemblerprogrammierung innerhalb von BASCOM hineingekniet.
Einiges über benutzte Register steht ja in der Bascom Hilfe. Wenn du tiefer in die Kombination Assembler/Bascom einsteigen möchtest, ist ein Reassembler echt eine super Hilfe. In dem Artikel, den Dino verlinkt hat, ist ein Link zu einem Reassembler (ReAVR) vorhanden.

Du erkennst nach ein klein wenig Übung sofort deinen eigenen Assemblercode und die Unterschiede zum Bascom-Compilat. Du siehst z.B. sofort, wie der Bascom-Softwarestack verwaltet wird, falls du Funktionen oder Unterprogramme = Subs verwendest. Hier wird im gesamten Programm der Y-Pointer (das Registerpaar R28/R29) zur Übergabe der Variablen (und des Funktionsergebnisses) verwendet. Falls du die in Assemblerroutinen innerhalb von BASCOM benutzen möchtest, solltest du sie sichern.

Du siehst dann mit dem Reassembler auch, dass BASCOM sehr häufig die Register R26,R27,R30,R31 (X- und Z-Pointer) benutzt. Die kannst du also ziemlich frei benutzen; ich benutze auch öfters R16 und R17. Mit den 6 Registern komm' ich schon ziemlich weit. Falls du noch ein paar mehr Register brauchst, lieber mal das eine oder andere auf'm Stack speichern.

Zum Thema ISR ein Vorschlag:
a.) entweder mit BASCOM (ohne NoSave) oder
b.) in Assembler (mit NoSave), aber ohne Aufrufe von Bascom-Routinen.

Schönen Abend noch und viel Spass
Werner :)
 
Hallo lotadaC :wavey: ,

ich hätte noch eine (aktuelle) Ergänzung. BASCOM übersetzt I/O- Assemblerbefehle, die sich nicht auf die Ports direkt beziehen, durch äquivalente Operationen im RAM Speicherbereich. Hierzu muss BASCOM ein Register verwenden, das auch nicht gerettet bzw. nicht restauriert wird. Nähere Informationen, z.B. welche I/O Befehle das sind, kann man in der Hilfe unter “Mixing ASM and BASIC“ (teilweise) nachlesen. Ich möchte hier 2 Beispiele anführen, die ich in meinem aktuellen Projekt “herausgefunden“ habe.

1.Beispiel:
Origialquelltext:

Code:
...
  SBI     MCUCR,ISC10           ' Trigger external Interrupt = Rising Edge
  SBI     MCUCR,ISC11           ' für INT1
...

Übersetzung BASCOM-Compiler (mit ReAVR reassembliert)

Code:
	...
	lds	r23,(p35+0x20)	; io register
	ori	r23,k04
	sts	(p35+0x20),r23	; io register
	lds	r23,(p35+0x20)	; io register
	ori	r23,k08
	sts	(p35+0x20),r23	; io register
	...

hierbei bedeuten :
k04 = Konstante 4,
p35 = Konstante (hexadezimal) des IO-Registers MCUCR (IO-Adresse)
p35 + 0x20 = Adresse MCUCR im RAM-Speicherbereich


2.Beispiel
Origialquelltext:

Code:
…
HandleTimer2_CTC3a:
  SBIC    TIMSK,OCIE2
  RJMP    HandleTimer2_CTC3a
...

Übersetzung BASCOM-Compiler (mit ReAVR reassembliert):
Code:
L00D6:
	…
	lds	r0,(p39+0x20)	; io register
	sbrc	r0,b7
	rjmp	L00D6
	…

hierbei bedeuten :
b7 = Konstante 7 (Bit),
p39 = Konstante (hexadezimal) des IO-Registers TIMSK (IO-Adresse)
p35 + 0x20 = Adresse TIMSK im RAM-Speicherbereich

In beiden Fällen werden die (erlaubten) IO-Operationen durch entsprechende RAM-Operationen ersetzt. Im ersten Fall wird das Register R23 von BASCOM verwendet, im zweiten Fall das Register R0. Das kann aber nur heißen, dass beide Register nicht längerfristig benötigt werden und so (außerhalb von Interrupt-Routinen) recht freizügig in eigenen Assemblerprogrammteilen verwendet werden können.


Im ersten Beispiel können die beiden Bits von Hand zusammengefasst werden. Dazu bräuchte man eine Shiftoperation (in C “<<“), die im BASCOM "Assemblerteil" jedoch nicht zur Verfügung steht. Es geht aber trotzdem, so dass die symbolischen Konstanten ISC10 (=2, gemeint ist Bit 2) und ISC11 (=3, gemeint ist Bit 3) weiter benutzt werden können. Die Lösung ergibt sich durch Verwendung der Exponentialschreibweise (Operator “^“).

Das folgende Beispiel erzeugt daher den verkürzten, äquivalenten Code von Beispiel 1:
Code:
  LDS     mReg,MCUCR+&H20
  ORI     mReg,2^ISC10 + 2^ISC11
  STS     MCUCR+&H20,mReg

Ich hoffe, das ergänzt diese Zusammenstellung.


Schönen Abend noch
Werner
 
Bezüglich der I/O-Befehle, wie z.B. SBI, SBIC, ..., die ich oben angesprochen habe:
In der "AVR Instruction Set" Dokumentation ist zu lesen, dass diese Befehle nur auf die unteren 32 Register angewendet werden können.

Im Datenblatt (z.B. des Mega8) gibt es ein Kapitel "Register Summary". Dort sind alle Register sowohl mit ihrer I/O- als auch ihrer RAM Adresse angegeben. Dort könnte man nachschauen, welche Register also diskret zulässig sind.

So sind PORTA, PORTB, ... zulässig, dagegen TIMSK, TCCR2, ... hingegen nicht. Im Sinne einer höheren Aufwärtskompatibilität, z.B. dass eine Anpassung meines Codes auf einen ATMega 644 einfacher möglich sein soll, werde ich zukünftig die RAM Adressierung bevorzugen.


LG Werner
 
Danke vorweg...
Und jetzt nochmal konkret nachgefragt:
betrifft die Veränderungen des Assemblerblockes nur unzulässige I/O-Bereiche (also wo auch der AVR-Assembler meckern würde), oder wird das generell umgeschrieben (außer bei den PORT/DDR)?
Was ist mit IN/OUT (reicht ja, wenn ich mich recht erinner bis 0x3F - SBI/CBI nur bis 0x1F) - dein Beispiel hatte ja nur STS/LDS

Achso, paßt hier irgendwie zwar nicht ganz rein, ist mir aber grad eingefallen: Die ersten 32 Bytes im RAM sind doch die Rechenregister, oder? Kommt man da mit der indirekten Adressierung über die Pointerregister ran? (also in Assembler im allgemeinen und in Bascom (+Assembler) im speziellen)
 
Hi lotadaC,

$ASM
nop
nop
nop
SBI PORTD,1
nop
nop

CLR ZH
LDI ZL,5 ' Adressiert Register R5
LDI R16,10
ST Z,R16
$END asm

wird zu

nop
nop
nop
sbi p12,b1
nop
nop
clr r31
ldi r30,k05
ldi r16,k0A
st Z,r16

also bei den Ports werden die IO-Befehle benutzt und Adressen < 32 kannst du wie RAM-Speicherstellen addressieren (kannst auch mal simulieren).
Also - ist echt ein gut gemeinter Rat - der ReAVR Reassembler lohnt sich allemal runterzuladen - weil "Versuch macht kluch".


LG Werner

P.S. Ich mach die nop's 'rein, damit ich die Stellen im Reassemblat schneller finde. Ein Compiler macht normal keine rein :D
 
Hi Werner,

und hier die beiden direkten Links ... aber leider ...
http://avr.jassenbaum.de/ja-tools/files/ReAVR320setup.exe
http://avr.jassenbaum.de/ja-tools/files/ReAVR320setup.zip
... kommt da dann nur sowas ...
Forbidden
You don't have permission to access /ja-tools/files/ReAVR320setup.exe on this server.
Apache/2.0.46 (CentOS) Server at avr.jassenbaum.de Port 80
:( :( :(
Tja ... will er wohl nicht mehr ...

Gruß
Dino
 
Ja so'n Mist :eek:, tut mir Leid.

bei den < AVR-Freaks> kann man's noch runterladen. Also bei den Freak-Tools (siehe Bild), nicht auf der Homepage des Autors.
Hab's gerade eben ausprobiert, es geht.

LG
Werner
 

Anhänge

  • temp.gif
    temp.gif
    78,1 KB · Aufrufe: 21
So, ich hab' mal wieder Urlaub :D,

und kann wieder meinem Hobby frönen. Es dauert aber immer seine Zeit, bis ich mich wieder in die BASCOM-Assemblerprogrammierung hineingefunden habe.

Ich hab' jetzt einen alten "BUG" gefunden (hat mich ca. 2 Tage gekostet :stoned:), der auf einem "Fehler" von BASCOM beruht. Laut Handbuch sollen ISR-Routinen (beim Erstellen in Assembler) mit einem "RETURN" abgeschlossen werden, welches vom Compiler dann durch ein "RETI" ersetzt wird; leider funktioniert das nicht zuverlässig, falls mehrere Interruptserviceroutinen eingebunden werden. Also lieber am Ende der ISR-Routine selbst ein "RETI" einfügen.

Schönen Tag noch
Werner
 
Erklär mal genauer.
Normalerweise wird in Bascom der Interruptvektor durch ein "On interruptquelle labelname" erzeugt, oder?
Der, dem label dann folgende Bascom-Code ist die ISR - das abschließende Return wird durch ein RETI übersetzt. So sollte es sein.
Platzierst Du jetzt darin einen ASM-Codeblock, sollte das doch keinen Unterschied machen, oder?
Was meinst Du konkret mit "mehrere ISR eingebunden"? Innerhalb ein und desselben ASM-Blockes?
Sind insbesondere die Labelnamen (ISR-Einsprung) innerhalb des ASM-Blockes? (Schluckt Bascom das überhaupt, oder findet er ddann den Label nicht?)

Frage nebenbei: darf ich mehrere RETI in eine ISR einbauen (um bei Verzweigungen ggf Sprünge zu sparen)? In Bascom meine ich - in ASM macht der Controller ja jeden erdenklichen Quark mit, den man programmiert...
 
Hi LotadaC,

ja, du kannst die RETI's mehrfach einbauen (kannst du unten in meinem Text sicher nachvollziehen).
Zunächst die Zusammenstellung meines Quelltextes in BASCOM:

1.) Deklaration der ISR-Routinen

Code:
...
ON INT1 OnInt1_ISR    NoSave
ON OC2  OnTimer2_CTC  NoSave
ON OC1A OnTimer1A_CTC NoSave
ON URXC _ReadRS232String_ISR NoSave  ' steht komplett in einer in library
...

2. Die Implementierung des Codes (BASCOM-Version)

Code:
Zeile 243:
'*******************************************************************************
OnInt1_ISR:                    '[B][COLOR="#FF0000"]HIER IST ES RICHTIG[/COLOR][/B]
$ASM                           ' *****************  ACHTUNG ********************
  IN      mReg,SREG            ' nur Statusregister wird gerettet
  ADIW    XL,1                 ' geänderte Register: mReg,XL,XH,YL,YH
  BRNE    OnInt0_ISR0
  ADIW    YL,1
OnInt0_ISR0:
  OUT     sreg,mReg            ' Statusregister wiederherstellen
$END ASM
Return
'*******************************************************************************

Zeile 391:
'*******************************************************************************
$ASM
OnTimer2_CTC:                  '[B][COLOR="#FF0000"]FEHLER : LABEL MUSS VOR ASM-Anweisung !!!!![/COLOR][/B]
  PUSH    mReg                 ' 1.Arbeitsregister 'mreg' retten
  IN      mReg,SREG            ' Statusregister einlesen
  PUSH    mReg                 ' und retten

  ...

  POP     mReg
  OUT     sreg,mReg            ' sowie Flags wiederherstellen
  POP     mReg
$END ASM
Return
'*******************************************************************************

Zeile 324:
'*******************************************************************************
$ASM
OnTimer1A_CTC:                  ' [B][COLOR="#FF0000"]FEHLER : LABEL MUSS VOR ASM-Anweisung !!!!![/COLOR][/B]
  PUSH    mReg                  ' 1.Arbeitsregister 'mreg' retten
  IN      mReg,SREG             ' Statusregister einlesen
  PUSH    mReg                  ' und retten

  ...

  POP     mReg
  OUT     sreg,mReg             ' sowie Flags wiederherstellen
  POP     mReg
$END ASM
Return
'*******************************************************************************

So, jetzt wo ich den Beitrag schreibe/poste hab' ich den Fehler, den ICH gemacht habe, selbst entdeckt: die Deklaration der Sprungmarke muss vor das ASM-Statement gezogen werden (ich hab' das Ergebnis überprüft, jetzt wird ein RETI erzeugt).
Somit hat sich der Rest erledigt.

Schönen Tag noch
Werner
 
Zur Vervollständigung: ich hatte als Workaround sämtliche Routinen in einen Block geschrieben. Das ging auch und der Code enthielt die entsprechenden RETI's - wurde von Bascom nicht moniert.

1.) Die Deklaration der Routinen war wie oben

2.) Der Originalcode:

Code:
'*******************************************************************************
OnInt1_ISR:
$ASM                           ' *****************  ACHTUNG ********************
  IN      mReg,SREG            ' nur Statusregister wird gerettet
  ADIW    XL,1                 ' geänderte Register: mReg,XL,XH,YL,YH
  BRNE    OnInt0_ISR0
  ADIW    YL,1
OnInt0_ISR0:
  OUT     sreg,mReg            ' Statusregister wiederherstellen
  RETI                         ' ES IST BESSER, DAS "RETI" SELBST ZUZUFÜGEN
'===============================================================================
'===============================================================================
'===============================================================================
OnTimer2_CTC:                  ' ############## Interrupthandler ##############
  PUSH    mReg                 ' 1.Arbeitsregister retten
  IN      mReg,SREG            ' Statusregister einlesen
  PUSH    mReg                 ' und sichern

  ...

  POP     mReg
  OUT     sreg,mReg            ' sowie flags wiederherstellen
  POP     mReg
  RETI                         ' ES IST BESSER, DAS "RETI" SELBST ZUZUFÜGEN
'===============================================================================
'===============================================================================
'===============================================================================
OnTimer1A_CTC:                 ' ############## Interrupthandler ##############
  PUSH    mReg                 ' 1.Arbeitsregister 'mreg' retten
  IN      mReg,SREG            ' Statusregister einlesen
  PUSH    mReg                 ' und retten

  ...

  POP     mReg
  OUT     sreg,mReg            ' sowie Flags wiederherstellen
  POP     mReg
  RETI                         ' ES IST BESSER, DAS "RETI" SELBST ZUZUFÜGEN
$END ASM
RETURN
'*******************************************************************************

3.) und was der Compiler daraus gemacht hat (das letzte RETURN wird tatsächlich umgewandelt !!)

Code:
'*******************************************************************************
;----------------------*
; pc=0x28B(0x516)
;
L028B:
	in	r16,p3F
	adiw	r26,k01
	 brne	L028F
;	-----		branch on last line
	adiw	r28,k01
L028F:
	out	p3F,r16
	reti
;----------------------*
; pc=0x291(0x522)
;
L0291:
	push	r16
	in	r16,p3F
	push	r16

        ...

	pop	r16
	out	p3F,r16
	pop	r16
	reti
;----------------------*
; pc=0x2A1(0x542)
;
L02A1:
	push	r16
	in	r16,p3F
	push	r16

        ...

	pop	r16
	out	p3F,r16
	pop	r16
	reti
;----------------------*
; pc=0x2B1(0x562)
;
	reti
;----------------------*

Das letzte RETI ist zwar doppelt ... aber, was soll's

LG Werner
 
Ok, das meinte ich mit den labelnamen oben.
Nebenbei hast Du also einen Weg gefunden, in Bascom Interrupts zu beenden, ohne selbige wieder freizugeben (RET).
Im ernst - interessant finde ich, daß er die label innerhalb des ASM-Blockes durch einen IRQ anspringen kann, aber er trotzdem das Return nicht umwandelt. Das ist irgendwie ... zu inkonsequent, um Absicht zu sein - hast Du mal bei MCS nachgefragt?
 
Ich schreibe hier mal einen Kommentar dazu, weil ich diesen "Label-Bug" jetzt wieder berücksichtigen muss. Will wieder 'was machen.
 

Über uns

  • Makerconnect ist ein Forum, welches wir ausschließlich für einen Gedankenaustausch und als Diskussionsplattform für Interessierte bereitstellen, welche sich privat, durch das Studium oder beruflich mit Mikrocontroller- und Kleinstrechnersystemen beschäftigen wollen oder müssen ;-)
  • Dirk
  • Du bist noch kein Mitglied in unserer freundlichen Community? Werde Teil von uns und registriere dich in unserem Forum.
  •  Registriere dich

User Menu

 Kaffeezeit

  • Wir arbeiten hart daran sicherzustellen, dass unser Forum permanent online und schnell erreichbar ist, unsere Forensoftware auf dem aktuellsten Stand ist und der Server regelmäßig gewartet wird. Auch die Themen Datensicherheit und Datenschutz sind uns wichtig und hier sind wir auch ständig aktiv. Alles in allem, sorgen wir uns darum, dass alles Drumherum stimmt :-)

    Dir gefällt das Forum und unsere Arbeit und du möchtest uns unterstützen? Unterstütze uns durch deine Premium-Mitgliedschaft!
    Wir freuen uns auch über eine Spende für unsere Kaffeekasse :-)
    Vielen Dank! :ciao:


     Spende uns! (Paypal)