ARM Assembler - String an serielle Schnittstelle ausgeben

OP #5461145
Lesenswert?

Hallo,
wir sollen eine Funktion in ARM Assembler programmieren die einen 
gegebenen string an die serielle schnittstelle ausgibt.

Eine put character Funktion haben wir gegeben und wir sollen eine put 
String Funktion schreiben. Das Zeichen bzw. der String der ausgegeben 
werden soll steht in r0 (nehm ich an). Der String terminiert mit '\0'.

Das hier ist die put String Funktion:
1
puts:
2
  stmfd sp!,{lr}  @ Retten der Register
3
  mov r4, r0     @ start-adresse von string (r0) in r4 laden
4
loop1:
5
  ldrb r0, [r4]  @ aktuelle adresse von string in r0 laden
6
  cmp r0, #0    @ ueberpruefen ob der string mit 0 terminiert
7
  beq fertig    @ falls ja sind wir fertig
8
  bl  putc    @ ansonsten putc aufrufen und zeichen an serielle schnittstelle uebertragen
9
  # cmp r0, #0    @ ????
10
  # beq loop1
11
  add r4, #1    @ adresse des strings um eins erhoehen um nächstes zeichen zu bekommen
12
  b loop1      @ zum anfang der loop springen
13
fertig
14
  ldmfd sp!,{pc}  @ Rücksprung

Problem ist, dass ich beim kompilieren die Fehlermeldung "bad 
instruction 'fertig'" bekomme. Also der bezieht sich dann auf diese
  ldmfd sp!,{pc}  @ Rücksprung
Zeile, aber ich weiß nicht was da falsch ist? Dieses ldmfd war auch 
schon vorgegeben und ich hab davor halt nur ein Marker "fertig" gesetzt, 
damit ich da hinspringen kann wenn der string endet.
OP #5461240
Lesenswert?

ah danke, ja jetzt kompiliert er es.

Stimmt die funktion denn ansonsten? Kann es momentan nicht testen weil 
ich den mikrocontroller hier nicht hab. Ich hab die lösung ehrlich 
gesagt von jemanden übernommen aber so wirklich sinn macht das ganze für 
mich nicht. die kommentare hab ich selber geschrieben aber zum beispiel 
diese zwei zeilen machen für mich keinen sinn:

  # cmp r0, #0    @ ?????
  # beq loop1

hab ich deswegen auch auskommentiert.
Weiß nicht wieso ich r0 nochmal auf 0 überprüfen soll nachdem ich putc 
aufgerufen hab.

Und ich weiß auch ohnehin nicht wie genau das funktionieren soll mit dem 
auf 0 überprüfen. laut aufgabenstellung:

void puts(char *) Ausgabe eines nullterminierten Strings und Ersetzung 
von Newline durch Carriage Return (0x0D) und Linefeed (0x0A).

Ok der string ist also nullterminierend, aber mit cmp r0, #0 überprüf 
ich doch nur ob da eine 0 drin steht aber ich überprüf doch nicht ob ein 
'\0' drin steht (falls da ein unterschied besteht und das sollte es ja 
sonst dürfte im string keine 0 vorkommen)? Und rein laut 
aufgabenstellung muss ich ja noch wenn in dem string am ende ein '\n' 
steht das durch 0x0D und 0x0A ersetzen, aber ich weiß nicht welcher wert 
in r0 stehen soll damit ich weiß das da '\n' drin steht?
Gast #5461245
Lesenswert?

Peter W. schrieb:

> Weiß nicht wieso ich r0 nochmal auf 0 überprüfen soll nachdem ich putc
> aufgerufen hab.

Das wird wohl der Rückgabewert der Funktion sein - 0 steht üblicherweise 
für OK. Die Ausgabe soll also abgebrochen werden, wenn putc() einen 
Fehler meldet.

> Ok der string ist also nullterminierend, aber mit cmp r0, #0 überprüf
> ich doch nur ob da eine 0 drin steht aber ich überprüf doch nicht ob ein
> '\0' drin steht

Das ist dasselbe.

> (falls da ein unterschied besteht und das sollte es ja
> sonst dürfte im string keine 0 vorkommen)?

Richtig, C-Strings dürfen keine 0 enthalten.

> aber ich weiß nicht welcher wert
> in r0 stehen soll damit ich weiß das da '\n' drin steht?

In C ist '\n' eine sog. Escape-Sequenz, und die hat einen ASCII-Wert:

https://en.wikipedia.org/wiki/Escape_sequences_in_C#Table_of_escape_sequences
#5461597
Lesenswert?

Ein Problem in deinem Code ist noch, dass du Register r4 verwendest, 
ohne es zu sichern (dein stmfd sichert nur das link register auf dem 
Stack).

Laut Procedure Call Standard for the ARM Architecture 
(http://infocenter.arm.com/help/topic/com.arm.doc.ihi0042f/IHI0042F_aapcs.pdf) 
ist r4 ein "callee saved"-Register, also von der aufgerufenen Funktion 
(dein puts) zu sichern.

Abhilfe:

* Entweder ein anderes Register nehmen, z.B. r1, r2 oder r3, die sind 
Parameter bzw. scratch register
* oder r4 bei stmfd mitsichern und bei ldmfd wiederherstellen

-- Michael
#5461608
Lesenswert?

Das Problem stinkt nach Uni, und da spielt die ABI keine Rolle.
Der Assembler-Code muss nicht mit C-Code zusammenarbeiten.

Aufrufkonventionen u.ä. habe ich in der Vorlesung angesprochen, aber 
sonst außer Acht gelassen. Relevant wird sowas erst in einer höheren 
Vorlesung.
#5462762
Lesenswert?

Nop schrieb:
> S. R. schrieb:
>> Das Problem stinkt nach Uni, und da spielt die ABI keine Rolle.
>
> Wenn r0 als Rückgabewert einer Funktion dient, wird zumindest dieser
> ABI-Teil offensichtlich ja schon verwendet.

R0 ist auch der erste Parameter und wenn man als Assembler-Programmierer 
auf Cortex-M mit Interrupts in Berührung kommt, müssen diese eh von 
C-Style-Funktionen behandelt werden. Da kann man ja dann gleich bei 
bleiben.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren