Forum: Compiler & IDEs Eine gemeinsame Konstante in C und asm?


von Bauform B. (bauformb)


Angehängte Dateien:

Lesenswert?

hallo und schöne Ostern!

Mal eine ganz einfache Frage: Man baut hier gerne asm in C-Funktionen 
ein. An einer Stelle kann man Konstanten gut per enum definieren. Wie 
kann man diese Konstanten auch in asm benutzen? Oder umgekehrt?

Dieses funktioniert zwar, aber wie sieht das aus und wie lange geht das 
gut?
1
// import-export.h -- Unterstützung für zweigeteilte Programme
2
  
3
#pragma once
4
5
__asm__ ("ASSERT_FUNC =  0 \n .global ASSERT_FUNC \n");
6
__asm__ ("CLOCK_SETUP =  1 \n .global CLOCK_SETUP \n");
7
__asm__ ("GETCHAR     =  2 \n .global GETCHAR     \n");
8
__asm__ ("PRINTF      =  3 \n .global PRINTF      \n");
9
__asm__ ("PUTCHAR     =  4 \n .global PUTCHAR     \n");
10
__asm__ ("PUTS        =  5 \n .global PUTS        \n");
11
__asm__ ("UART_INIT   =  6 \n .global UART_INIT   \n");
12
13
typedef enum import_export_enum {
14
   ASSERT_FUNC,
15
   CLOCK_SETUP,
16
   GETCHAR,
17
   PRINTF,
18
   PUTCHAR,
19
   PUTS,
20
   UART_INIT
21
} import_export_enum;

von A. S. (Gast)


Lesenswert?

Der Code sieht mir eher so aus, als würdest Du in Assembler und in C 
einfach 2 Sätze von "Konstanten" erstellen, die u.A. auch gleich sein 
dürfen.

von Bauform B. (bauformb)


Lesenswert?

A. S. schrieb:
> 2 Sätze von "Konstanten" erstellen, die u.A. auch gleich sein dürfen.

Nix "dürfen", müssen!

von W.S. (Gast)


Lesenswert?

Bauform B. schrieb:
> Man baut hier gerne asm in C-Funktionen
> ein.

So?
Wer tut das denn "hier gerne"?

Ich nehme mal an, daß das wieder mal eine Art Gedankenexperiment ohne 
praktische Anwendung ist.

Normalerweise schafft man sich möglichst sauber definierte 
Schnittstellen zwischen den verschiedenen Programmteilen bzw. Ebenen. Da 
sind dann .h Dateien zu den zugrundeliegenden Assemblerteilen nötig, 
wenn man die in Assembler geschriebenen Funktionen in C verfügbar machen 
will. Aber gemeinsame Konstanten? Wohl eher nicht.

W.S.

von Bauform B. (bauformb)


Lesenswert?

W.S. schrieb:
> Ich nehme mal an, daß das wieder mal eine Art Gedankenexperiment ohne
> praktische Anwendung ist.

Die praktische Anwendung ist ein UART-Bootloader für den STM32L031C4. 
Dem Chip fehlt es an allem (wenn man vom L4 verwöhnt ist). Zum Beispiel 
kann der eingebaute Bootloader keine Programme ins RAM laden. Bei 8K RAM 
ist es eben günstig, wenn das Programm im RAM Funktionen nutzen kann, 
die im Bootloader im Flash sowieso vorhanden sind.

> Normalerweise schafft man sich möglichst sauber definierte
> Schnittstellen zwischen den verschiedenen Programmteilen bzw. Ebenen.

Die Schnittstelle fand ich schon sauber, die Sache ist ja eigentlich 
trivial. Es fehlte nur die eine Kleinigkeit. Inzwischen sieht es besser 
aus, jetzt wird C nur noch benutzt um die diversen Attribute für die asm 
section lesbarer zu machen. Die eigentliche Aufgabe war ja, die Offsets 
in einer *.h zu haben, die von beiden Programmen included wird. Jetzt 
ist das kein Problem mehr, weil alles Assembler ist. Ich finde es so 
sogar übersichtlicher.

von c-hater (Gast)


Lesenswert?

Bauform B. schrieb:

> Die eigentliche Aufgabe war ja, die Offsets
> in einer *.h zu haben, die von beiden Programmen included wird. Jetzt
> ist das kein Problem mehr, weil alles Assembler ist. Ich finde es so
> sogar übersichtlicher.

Ja, genau meine Erfahrung: macht man alles in Assembler, wird's am Ende 
deutlich übersichtlicher. Jedenfalls so lange man "hardwarenah" agiert. 
Und das ist schon immer dann der Fall, wenn man das strunzdumme und 
primitive C-Speichermodell verlässt...

Also auf heutigen realen µC-Systemen praktisch immer.

Nur Ausweichen auf ein OS löst das Problem der Dummen und Inkompetenten. 
Also das der reinen App-Bastler. Aber OK, wenn die Basis da ist, bin 
auch ich nur noch App-Bastler. Genehmige mir dann aber typisch auch 
einen großen Schluck aus der Pulle und quäle mich nicht mit C/C++ rum, 
sondern benutze das sehr viel durchdachtere und komfortablere DotNet. 
Nunja, es hat gewisse Performance-Nachteile gegenüber nativen Compilern. 
Aber drauf geschissen, der Beschluss war ja: wir nehmen ein dickes 
Eisen, um all unsere Probleme zu lösen. Also, wenn schon sinnlos 
prassen, dann richtig.

Die Python-Typen werden mir hier absolut zustimmen... ;o)

von Klaus W. (mfgkw)


Lesenswert?

Eine const-Variable, von der man eine Adresse bilden kann, kann über den 
Linker auch von Asm aus genutzt werden.

Eine enum gehört nicht dazu.

Wenn ich sowas bräuchte, würde ich mir mit irgendeinem Praprozessor 
entsprechende Definitionen für beide Seiten aus einer Datei generieren.

Oder man macht es mit einer Header-Datei mit #defines für beide 
Sprachen, aber dann muss man halt mit #defines leben. Mag ich nicht ..

c-hater schrieb:
> Die Python-Typen werden mir hier absolut zustimmen... ;o)

Naja, vielleicht hast du dich ja bemüht, etwas sinnvolles beizutragen?

von Bauform B. (bauformb)


Lesenswert?

Klaus W. schrieb:
> Eine const-Variable, von der man eine Adresse bilden kann, kann über den
> Linker auch von Asm aus genutzt werden.

Nett! Also so ungefähr? impex.h:
1
typedef enum import_export_enum {
2
   GETCHAR,
3
   PRINTF,
4
   PUTCHAR,
5
} import_export_enum;
6
7
const int *idx_printf = (int *)0 + PRINTF; __attribute__ ((section (".ABS.")))
exports.c:
1
#include "impex.h"
2
extern int  getchar;
3
extern int  printf;
4
extern int  putchar;
5
6
const void  __attribute__ ((section ("$exports"), aligned (128)))
7
*exports[] = {
8
   [ GETCHAR     ] = &getchar,
9
   [ PRINTF      ] = &printf,
10
   [ PUTCHAR     ] = &putchar,
11
};
imports.c:
1
#include "impex.h"
2
int __attribute__ ((naked, aligned (4)))
3
imports (void)
4
{
5
   __asm__ (
6
      "jump:   ldr     r1, [pc, #8]\n"
7
      "        ldr     r0, [r1, r0]\n"
8
      "        mov     ip, r0\n"
9
      "        pop     {r0, r1}\n"
10
      "        bx      ip\n"
11
      "        nop\n"
12
      "        .word   0x20000080\n"
13
      "getchar:\n"
14
      "        .global getchar\n"
15
      "        push    {r0, r1}\n"
16
      "        movs    r0, # idx_getchar\n"
17
      "        b       jump\n"
18
// usw.
19
   );
20
}

Es fehlt noch an zwei Stellen:
trotz _attribute_ ((section (".ABS."))) kommen die Konstanten so raus:
1
BUILD/boot.map:  0x20006208  idx_putchar
gewünscht hätte ich mir 0x08. Der Wert wird ja als immediate für movs 
gebraucht. Und da gibt es das zweite Problem: der as merkt, dass das 
keine absoluten Konstanten sind.

von Armin (Gast)


Lesenswert?

Bauform B. schrieb:
> Mal eine ganz einfache Frage: Man baut hier gerne asm in C-Funktionen
> ein.

Ich halte das generell für keine gute Idee. Schon aus Gründen der 
Portabilität würde ich Assembler-Spielchen meiden. Wenn es denn an der 
"Performance" kranken sollte, würde ich auf einen leitungsfähigeren 
Prozessor setzen.

> An einer Stelle kann man Konstanten gut per enum definieren. Wie
> kann man diese Konstanten auch in asm benutzen? Oder umgekehrt?

Wenn ich denn sowas machen müsste und wollte (!), würde ich mich einzig 
auf den Preprozessor (1) verlassen, sprich auf Directives wie "#defines" 
und "#if"...

-Armin

(1) Und dann gibt es ja noch M4 und Konsorten ...

von W.S. (Gast)


Lesenswert?

Klaus W. schrieb:
> Eine const-Variable, von der man eine Adresse bilden kann, kann über den
> Linker auch von Asm aus genutzt werden.

Gröhl...

Also, was du meinst, ist eine im Code lokalisierte Variable. Also damit 
(im Pascal Slang) eine typisierte Konstante.
Etwa so:
const
  ottokar : integer = 4711;

Das ist eine Konstante, die eine Adresse in ihrem Speichersegment hat, 
was für gewöhnlich der Flash in einem Mikrocontroller ist.

Allerdings ist das etwas anderes, als der TO beabsichtgte. Der will 
lediglich im C-Teil sowas haben:
#define ottokar 4711
und im Assemblerteil sowas:
ottokar: EQU 4711

OK, wie das im jeweiligen Assemblerformat konkret aussehen muß, ist 
unteschiedlich je nach Plattform und Toolchain.

W.S.

von Bauform B. (bauformb)


Lesenswert?

Armin schrieb:
> Schon aus Gründen der Portabilität würde ich Assembler-Spielchen
> meiden.

Die Alternative in C wäre hier vielleicht ein longjmp() im RAM-Programm 
mit dem setjmp() im Flash-Programm. In Fortran gab es ein computed goto, 
das wäre ziemlich übersichtlich. Zwecks der Gaudi sollte ich sowas 
direkt mal probieren :)

Wahrscheinlich kann man auch einen Buffer Overflow nutzen. Dabei würde 
man den Maschinencode in ein C-array schreiben müssen, es wäre also 
nichts gewonnen und noch dazu wäre es von der Compiler-Version abhängig. 
Also, manchmal scheint mir Assembler viel übersichtlicher zu sein.

> Wenn ich denn sowas machen müsste und wollte (!), würde ich mich einzig
> auf den Preprozessor (1) verlassen, sprich auf Directives wie "#defines"
> und "#if"...

Das war eigentlich die meine Frage, ich hab' einiges erfolglos mit 
Macros probiert. M4 wäre allerdings eine echte Alternative, oder, für so 
simple Sachen wie hier, einfach ein Script. Das hat mir schon oft 
geholfen, aber solange es rein mit "Bordmitteln" geht...

von Bauform B. (bauformb)


Lesenswert?

W.S. schrieb:
> Allerdings ist das etwas anderes, als der TO beabsichtgte. Der will
> lediglich im C-Teil sowas haben:
> #define ottokar 4711
> und im Assemblerteil sowas:
> ottokar: EQU 4711

Vor allem darf die 4711 nur an einer Stelle auftauchen.

von Rolf M. (rmagnus)


Lesenswert?

Bauform B. schrieb:
> In Fortran gab es ein computed goto, das wäre ziemlich übersichtlich.
> Zwecks der Gaudi sollte ich sowas direkt mal probieren :)

gcc kennt das auch als Erweiterung für C.
https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html
Das Sprungziel muss aber in der selben Funktion liegen wie das goto.

: Bearbeitet durch User
von Armin (Gast)


Lesenswert?

Kurzer Test:

--- Vorgeplänkel

main.c:
1
#include <stdio.h>
2
void main()
3
{
4
    puts("hello world\n");
5
}

compiliert mit cc -S main.c -> wirft main.s aus. Dieses wird umbenannt 
in main.sx, also ein Assembler-File, das noch durch den Preprozessor 
(cpp) muss.

main.s:
1
  .file  "main.c"
2
  .text
3
  .section  .rodata
4
.LC0:
5
  .string  "hello world\n"
6
  [usw ...]

Dort habe ich ergänzt/geändert:

main.sx:
1
#define T_TEXT "Hello World\n"
2
3
  .file  "main.c"
4
  .text
5
  .section  .rodata
6
.LC0:
7
  .string  T_TEXT
8
  [usw ...]

Vorgeplänkel Ende ---

cc main.sx wirft dann das Executable a.out aus.

In der Praxis würde man natürlich #define T_TEXT "Hello World\n" in ein 
Headerfile auslagern und dieses #includen - im .c oder .ax file.

(#include wird auch vom cpp prozesst...)

Alternativ:

Assemblercode als Inline Code im C-file formulieren.

von Bauform B. (bauformb)


Lesenswert?

Rolf M. schrieb:
> gcc kennt das auch als Erweiterung für C.
> https://gcc.gnu.org/onlinedocs/gcc/Labels-as-Values.html

Unglaublich, dankeschön. Das kann ich bestimmt missbrauchen ;)

> Das Sprungziel muss aber in der selben Funktion liegen wie das goto.

Schwach. Hier liegt das Ziel in einem anderen Programm. Immerhin nicht 
auf einem anderen Rechner. Dabei fällt mir ein, ein goto reicht ja nie, 
ich will ja z.B. printf() ganz normal aufrufen.


Armin schrieb:
> main.sx, also ein Assembler-File, das noch durch den Preprozessor
> (cpp) muss.

Danke! Das ist wohl die natürlichste Lösung. Und eine gute Gelegenheit, 
mein import.c komplett in *.sx-Assembler zu schreiben. Inline-Assembler 
will man doch nur im Notfall. Den Zwischenschritt "cc -S main.c -> wirft 
main.s aus" würde ich (nur) nutzen, um die richtigen sections und 
-Attribute zu finden.

Nur das #define statt enum stört ein wenig; das Programm könnte sonst 
100% #define-frei sein. Aber es ist ja für einen guten Zweck ;)

von Zeno (Gast)


Lesenswert?

c-hater schrieb:
> sondern benutze das sehr viel durchdachtere und komfortablere DotNet.
DotNet? Oh mir schauderts. Bin froh das ich das nunmehr links liegen 
lassen darf. Aber ich muß Dir dennoch recht geben, gegenüber C/C++ ist 
es deutlich durchdachter und auch komfortabler. Leider wurden nicht alle 
guten Ansätze konsequent bis zum (bitteren) Ende durchgezogen.

von Bauform B. (bauformb)


Lesenswert?

Bauform B. schrieb:
> Armin schrieb:
>> main.sx, also ein Assembler-File, das noch durch den Preprozessor
>> (cpp) muss.
>
> Danke! Das ist wohl die natürlichste Lösung. Und eine gute Gelegenheit,
> mein import.c komplett in *.sx-Assembler zu schreiben.

Naja, das "natürlich" nehme ich zurück. So ein Assembler-Quelltext ohne 
C-Hülle drum herum ist nichts für schwache Nerven. Alleine 9 Zeilen in 
der Art ".eabi_attribute 30, 4" oder "code 16" kombiniert mit mehreren 
thumb-irgendwas -- das mag ich dann doch nicht lernen. Also brauche ich 
doch den Umweg .c -> .sx -> o.

Dafür sieht ein #include in der .c jetzt lustig aus:
1
__asm__ ("#include \"import-export.h\"");
 Ein normales include wird ja vom C-preprocessor platt gemacht.

von Armin (Gast)


Lesenswert?

Bauform B. schrieb:
> Dafür sieht ein #include in der .c jetzt lustig aus:__asm__ ("#include
> \"import-export.h\"");
>  Ein normales include wird ja vom C-preprocessor platt gemacht.

Kuck dir hierzu mal 
https://stackoverflow.com/questions/52525630/define-in-inline-assembly-in-gcc 
an.

Bauform B. schrieb:
> Naja, das "natürlich" nehme ich zurück. So ein Assembler-Quelltext ohne
> C-Hülle drum herum ist nichts für schwache Nerven. Alleine 9 Zeilen in
> der Art ".eabi_attribute 30, 4" oder "code 16" kombiniert mit mehreren
> thumb-irgendwas

Nimm mal eines deiner bestehende .s-files, benenne es um in .sx - und 
versuch dich an #define. (Mein Vorgeplänkel brauchst du ja nicht.)

https://www.ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html könnte 
dich auch interessieren. (Kap. 7 bzw Kap 7.5)

Falls es GCC (GNU Compiler Collection) nicht tut, muss eh MAKE ran. Dann 
bist nicht mehr an CPP gebunden.

von Armin (Gast)


Lesenswert?


von Bauform B. (bauformb)


Angehängte Dateien:

Lesenswert?

Armin schrieb:
> Nimm mal eines deiner bestehende .s-files, benenne es um in .sx - und
> versuch dich an #define. (Mein Vorgeplänkel brauchst du ja nicht.)

Ich nicht, aber die Maschine braucht ganz viel davon, viel mehr als dein 
Vorgeplänkel.

> Falls es GCC (GNU Compiler Collection) nicht tut, muss eh MAKE ran. Dann
> bist nicht mehr an CPP gebunden.

Ich glaube die tut durchaus. Inzwischen erzeuge ich mit ganz normalen 
Macros inline Assembler. Dabei kommt genau das raus, was ich mir 
vorgestellt hatte. Vor allem sorgen die Macros dafür, dass der Import 
automatisch zum Export passt. Na gut, die Macros sehen aus wie, nun ja, 
muss man nicht mögen...

Natürlich funktioniert das nur so lange, wie beide Programme die gleiche 
Version des Headers verwenden. Der Bootloader kann das jetzt prüfen, 
weil die mtime der Header-Datei mit eingebaut wird. Dafür brauche ich 
noch eine Zeile im Makefile, obwohl der GCC ein Macro namens __ 
TIMESTAMP __ anbietet. Leider liefert das wohl die mtime der c-Datei und 
nicht die des Headers.

von Armin (Gast)


Angehängte Dateien:

Lesenswert?

Mehrere Programme (Programmteile) unterhalten sich über ein festgelegtes 
Protokoll - welches sich leider ab und zu ändert...

Wenn (nicht nur) ich sowas implementiere, gebe ich den Protokoll einen 
Versions-Kenner mit - sinnvollerweise gleich als erstes Zeichen. Das 
mache ich sogar, wenn ich im Leben nicht dran denke, daran je mal wieder 
was zu ändern.

Diese Geitzkrägen bei der Internet Engineering Task Force (IETF) haben 
dafür nur 4bit vorgesehen. Ich nehme immer 1 Byte - und bilde mir ein, 
egal wie großzügig ich mit den Versionen um mich werfe, dass ich damit 
auskommen werde.

Vielleicht wäre also ein
 #define PROTOCOL_IC 1
in deiner import-export.h eine brauchbare Option?


Wenn es dir um "Sprungadressen" geht, könnte eine Adressleiste wie bei 
den  CP/M'schen BDOS-Systemcalls eine Option sein, um die Leiste über 
längere Zeit konstant zu halten?

Die BDOS-Calls verwendeten eine Systemcall-Kennung im 8080-C-Register, 
welche dann über eine Sprungleiste die richtige Funktion aufgerufen 
hatte.

Der DOS-Interrupt 21h funktionierte so ähnlich ...

von Bauform B. (bauformb)


Lesenswert?

Armin schrieb:
> Wenn (nicht nur) ich sowas implementiere, gebe ich den Protokoll einen
> Versions-Kenner mit - sinnvollerweise gleich als erstes Zeichen.

Genau so. Bei mir ist es ein 32-Bit Wort, weil da die Unix-Zeit 1:1 rein 
passt. Wenn ich jede Sekunde eine neue Version baue, reicht das 
mindestens bis 2038, praktisch bis Februar 2106 ;) Der entscheidende 
Vorteil: bei jeder Änderung wird zwangsweise eine neue Versionsnummer 
vergeben.

> Wenn es dir um "Sprungadressen" geht, könnte eine Adressleiste wie bei
> den  CP/M'schen BDOS-Systemcalls eine Option sein, um die Leiste über
> längere Zeit konstant zu halten?

Genau so wird's gemacht. Die Adressleiste selbst bleibt solange 
konstant, bis ein neuer "Syscall" dazu kommt. Und weil man den am Ende 
anhängt, merkt ein altes Programm immer noch keinen Unterschied. Der 
Inhalt der Leiste darf sich beliebig oft ändern.

von W.S. (Gast)


Lesenswert?

Armin schrieb:
> Wenn es dir um "Sprungadressen" geht, könnte eine Adressleiste wie bei
> den  CP/M'schen BDOS-Systemcalls eine Option sein, um die Leiste über
> längere Zeit konstant zu halten?

Ich schätze mal, daß du zu jung bist, um noch CP/M kennengelernt zu 
haben.
Also: die BDOS-Systemcalls wurden per CALL 5 erreicht und die gewünschte 
BDOS-Funktion wurde im C Register übergeben.

Genau SO war das damals.

W.S.

von Armin (Gast)


Angehängte Dateien:

Lesenswert?

W.S. schrieb:
> Ich schätze mal, daß du zu jung bist, um noch CP/M kennengelernt zu
> haben.
> [...]
> Genau SO war das damals.

Und wurde GENAU auf SO WAS ausgeliefert...

-A

von Armin (Gast)


Lesenswert?

Armin schrieb:
> Wenn es dir um "Sprungadressen" geht, könnte eine Adressleiste wie bei
> den  CP/M'schen BDOS-Systemcalls eine Option sein, um die Leiste über
> längere Zeit konstant zu halten?
>
> Die BDOS-Calls verwendeten eine Systemcall-Kennung im 8080-C-Register,
> welche dann über eine Sprungleiste die richtige Funktion aufgerufen
> hatte.

@Bauform B:
Um das etwas genauer zu erklären, habe ich mal die CP/M 2.2 BDOS-Sourcen 
ausgegbraben - und hier abgekürzt wiedergegeben:
1
;
2
; The function number desired is in register (C).
3
;
4
FBASE:  [...]
5
  LD  A,C    ;get function number.
6
  CP  NFUNCTS    ;valid function number?
7
  RET  NC
8
 ; ---> FEHLERAUSGANG
9
10
  LD  C,E    ;keep single register function here.
11
  LD  HL,FUNCTNS  ;now look thru the function table.
12
  LD  E,A
13
  LD  D,0    ;(DE)=function number.
14
  ADD  HL,DE
15
  ADD  HL,DE    ;(HL)=(start of table)+2*(function number).
16
  LD  E,(HL)
17
  INC  HL
18
  LD  D,(HL)    ;now (DE)=address for this function.
19
  LD  HL,(PARAMS)  ;retrieve parameters.
20
  EX  DE,HL    ;now (DE) has the original parameters.
21
22
 ; ---> berechnet die Adresse der durch (C) bezeichneten Funktion.
23
 ; Die Adresse der Funktion steht in (HL)
24
 ; Die Liste unter FUNCTS beinhaltet aufsteigend die Funktionsadressen
25
26
  JP  (HL)    ;execute desired function.
27
;
28
;   BDOS function jump table.
29
;
30
NFUNCTS EQU  41    ;number of functions in followin table.
31
;
32
FUNCTNS:DEFW  WBOOT,GETCON,OUTCON,GETRDR,PUNCH,LIST,DIRCIO,GETIOB
33
  DEFW  SETIOB,PRTSTR,RDBUFF,GETCSTS,GETVER,RSTDSK,SETDSK,OPENFIL
34
  DEFW  CLOSEFIL,GETFST,GETNXT,DELFILE,READSEQ,WRTSEQ,FCREATE
35
  DEFW  RENFILE,GETLOG,GETCRNT,PUTDMA,GETALOC,WRTPRTD,GETROV,SETATTR
36
  DEFW  GETPARM,GETUSER,RDRANDOM,WTRANDOM,FILESIZE,SETRAN,LOGOFF,RTN
37
  DEFW  RTN,WTSPECL

Heute schreibt man das in "drei" Zeilen ...

Wozu nun der ganze Aufwand?
Solange keine neuen Funktionen dazu kommen (oder alte sich ändern), 
bleibt auf diese Weise die BDOS-Schnittstelle gleich. Jedenfalls bei 
CP/M (und später bei DOS) hatte sich das Einfrieren der Schnittstelle 
bewährt.

von W.S. (Gast)


Lesenswert?

Armin schrieb:
> Wozu nun der ganze Aufwand?

Da sind wir wieder beim Ausgangsthema: Nicht das gemeinsame Benutzen von 
irgendwelchen Konstanten ist die Lösung für Verbindungen zwischen 
verschiedenen Ebenen (wie hier Funktionalität in Assembler und dito in 
C), sondern das saubere Ausarbeiten von sinnvollen Schnittstellen und 
eine sinnvolle Planung der Ebenen-Struktur in der zu schreibenden 
Firmware.

Und Sprungleisten (also Arrays von Maschinencode-Stücken) sind ein 
Notbehelf und keine wirkliche Lösung.

Nochwas: bei der damaligen Lernbetty hatten wir das mittels SVC (also 
dem Supervisor-Call bei ARM-Controllern) gelöst. Das ist quasi ein 
Interrupt, bloß der GCC kann das nicht.

W.S.

von Bauform B. (bauformb)


Lesenswert?

Armin schrieb:
> Solange keine neuen Funktionen dazu kommen (oder alte sich ändern),
> bleibt auf diese Weise die BDOS-Schnittstelle gleich. Jedenfalls bei
> CP/M (und später bei DOS) hatte sich das Einfrieren der Schnittstelle
> bewährt.

Das ist immer ein guter Plan. Schau dir die Schnittstelle 
Linux-Kernel/Userland an, da ist "kompatibel bleiben" das 1. Gebot seit 
20 Jahren.

W.S. schrieb:
> Und Sprungleisten (also Arrays von Maschinencode-Stücken) sind ein
> Notbehelf und keine wirkliche Lösung.
>
> bei der damaligen Lernbetty hatten wir das mittels SVC gelöst.

Ist das ein großer Unterschied? Innendrin hast du wieder eine Liste, die 
beiden Partnern bekannt sein muss und die sich nicht ändern sollte. Die 
SVC-Nummern entsprechen 1:1 meinen Problem-Konstanten. Ob ich die 
Konstanten jetzt für ein movs oder für ein svc Immediate brauche, macht 
doch wirklich keinen Unterschied. Der Verwaltungsaufwand ist gleich, 
aber was Laufzeit und Stack-Verbrauch angeht ist meine Lösung klar im 
Vorteil.

> Das [SVC] ist quasi ein Interrupt, bloß der GCC kann das nicht.

Ich hab' ihm erklärt, ein wenig asm tut garnicht weh, schau mal, so geht 
das. Und er hat es brav ausgeführt ;)

von Thomas Z. (usbman)


Lesenswert?

W.S. schrieb:
> #define ottokar 4711
> und im Assemblerteil sowas:
> ottokar: EQU 4711

nun ja das hängt sehr von der Toolchain ab
bei Keil Compilern/Assembler geht ein #define ottokar 4711 auch in 
Assembler.
Ich benutze das um c Header auch in ASM zu verwenden. Das ist aber auf 
wenige c macros beschränkt und ganz sicher nicht portabel.

von W.S. (Gast)


Lesenswert?

Bauform B. schrieb:
> Ist das ein großer Unterschied?

Ja.

W.S.

von W.S. (Gast)


Lesenswert?

Thomas Z. schrieb:
> nun ja das hängt sehr von der Toolchain ab
> bei Keil Compilern/Assembler geht ein #define ottokar 4711 auch in
> Assembler.

Das weiß ich.
Aber hier geht es um Prinzipien und nicht um eine spezielle Toolchain. 
Deshalb hab ich beides (ASM und C) jeweils so etwa in der Form 
geschrieben, wie es so einigermaßen typisch ist und deshalb wohl von den 
meisten Lesern verstanden wird.

W.S.

von Bauform B. (bauformb)


Lesenswert?

W.S. schrieb:
> Bauform B. schrieb:
>> Ist das ein großer Unterschied?
>
> Ja.

Das erklärt alles, danke!

von W.S. (Gast)


Lesenswert?

Bauform B. schrieb:
> Das erklärt alles, danke!

O ja, bittesehr.

Für etwaige Mitleser: Der SVC, also der Supervisor-Call ist ein 
besonderer Maschinenbefehl bei ARM-Befehlssätzen. Er erzeugt quasi einen 
Interrupt und braucht deshalb keinerlei Adress-Angabe. Der 
Maschinenbefehl enthält ein Bitfeld, wo man eine Botschaft oder 
Funktionsnummer oder so etwas ähnliches unterbringen kann. Bei diesem 
Quasi-Interrupt wird auch der Stack umgeschaltet (vom User-Stack auf den 
Supervisor-Stack). Die zugehörige ISR kann das Bitfeld auswerten und 
entsprechende Aktionen veranlassen oder eine Fehlerbehandlung starten.

Sowas ist vergleichbar mit der DOS-Schnittstelle "INT 21h", allerdings 
mit dem Unterschied, daß beim SVC die CPU vom Usermodus in den 
Supervisormodus wechselt und auch der Stack gewechselt wird. Näheres 
kann man bei Arm nachlesen.

W.S.

von W.S. (Gast)


Lesenswert?

Nachtrag, da ich Verständnis-Probleme wittere:

Bauform B. schrieb:
> Innendrin hast du wieder eine Liste, die
> beiden Partnern bekannt sein muss

Nein, das ist so nicht, sondern: das aufgerufene Programm hat eine Art 
API, das veröffentlicht ist und einer zu schreibenden Anwendung bekannt 
gemacht werden muß, damit die Anwendung das API benutzen kann.

Ob da "innendrin" ein kleiner grüner Buchhalter vom Mars werkelt oder ob 
das anders gemacht ist, ist völlig unerheblich. Deshalb sind auch die 
Innereien hinter der Pforte, die hier SVC heißt und woanders Int21h, 
kein Teil der API-Dokumentation. Ob da nun anhand einer Liste von 
Funktionen irgendwohin verzweigt wird oder es eben anders gemacht wird 
(s.o.), ist schnurz.

Ein API ist eine Schnittstelle und als solche dient es dazu, daß zwei 
völlig unterschiedliche Programme miteinander kommunizieren können, und 
das sogar ohne wissen zu müssen, wo wer lokalisiert ist - und ohne 
gemeinsam gelinkt worden zu sein.

Nochwas: Eine Sprungleiste sieht im Prinzip etwa so aus:
Sprungleiste:
{ goto ErsteFunktion;
  goto ZweiteFunktion;
  goto DritteFunktion;
  ...usw.
}

Bei sowas muß man sowohl die Adresse der Sprungleiste kennen, als auch 
den Umfang der Elemente, damit man anhand eines Index den Anfang des 
gewünschten Elements berechnen kann.

W.S.

von Bauform B. (bauformb)


Lesenswert?

Na, meinetwegen verstecke ich die "Benutzer"-Seite in einer library. 
Dann merkt man nicht mehr, ob innen drin ein Sprung oder ein SVC 
werkelt. Der Benutzer sieht nur sowas wie "int putchar (int)", das wäre 
zwar ohne library genauso, aber mit ist es nicht so fremdartig.

Nur, innen drin muss immer noch die korrekte Zuordnung passieren, egal 
ob SVC oder Sprung, library oder nicht.

von Bernd (Gast)


Lesenswert?

Sprungleiste...
Die ganze Zeit musste ich grübeln, was er damit wohl meint.
Bei mir hieß das Ding immer Sprungtablle oder jump table.

von W.S. (Gast)


Lesenswert?

Bernd schrieb:
> Sprungleiste...
> Die ganze Zeit musste ich grübeln, was er damit wohl meint.
> Bei mir hieß das Ding immer Sprungtablle oder jump table.

Da kommt mir ein zorniger Leserbrief in der Funkschau der 70er Jahre in 
den Sinn: Ein Kunde wollte sich ein neues Radio kaufen und fragte den 
Verkäufer nach den technischen Daten, ob es sich z.B. bei der Kurzwelle 
um einen Doppelsuper handelt. Antwort des Verkäufers: "Mein Herr, so 
etwas wie Super gab es mal früher, das ist veraltet. Wir verkaufen nur 
noch RECEIVER!"

Fachleute eben...

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.