Robotrontechnik-Forum

Registrieren || Einloggen || Hilfe/FAQ || Suche || Mitglieder || Home || Statistik || Kalender || Admins Willkommen Gast! RSS

Robotrontechnik-Forum » Technische Diskussionen » Beispiel für Parameter via Stack im Z80 » Themenansicht

Autor Thread - Seiten: -1-
000
12.06.2019, 19:02 Uhr
Mario Blunk

Avatar von Mario Blunk

Bevor ich mir das mühsam zusammenreime, frage ich in die Runde. Hat jemand ein sehr einfaches Beispiel, wie über den Stack ein Parameter an eine Routine übergeben wird ? So einfach wie unten ist es nicht, weil das "pop af" die Rücksprungadresse vom Stack holt. Wie sichert man vernünfigerweise die Rückkehradresse ? In einem Register ? Irgendwo im RAM ?

rout_1:
; macht irgendwas mit einem übergebenen Byte
pop af ; das geht nicht, weil die Rückkehradresse ganz oben auf dem Stack liegt
sub a, 4
push af; stack wieder korregieren, aber falsche Rückkehradresse wird abgelegt
ret

main:
ld a, 88h
push af
call rout_1
...
--
Mein Chef ist ein jüdischer Zimmermann.
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
001
12.06.2019, 19:46 Uhr
ambrosius



Als erstes würde ich den Interrupt ausschalten, falls der benutzt wird, um die zwischenzeitliche Nutzung des Stacks zu unterbinden.
di
Danach holst Du die Rücksprungadresse vom Stack, z.B.
pop hl
danach das zu übernehmende Byte
pop af
dann die Rückkehradresse wieder sichern
push hl
Danach kannst Du auch den Interrupt wieder einschalten.
ei
Danach hast Du die 88h (aus obigem Beispiel) in af und die Rückkehradresse wieder auf dem Stack. Sollen es mehrere Bytes sein, dann entsprechend hl im RAM sichern.
--
Viele Grüße
Holger
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
002
12.06.2019, 19:52 Uhr
Steffen.111

Avatar von Steffen.111

Hallo Mario,
Du musst an der Stelle, wo Du den Parameter benötigst, zunächst die Rückkehradresse popen, danach den Parameter. Anschließenden musst Du die Rückkehradresse mit push wiederum auf den Stack legen.
Fertig.
Ggf. Den Befehl EX (SP),HL verwenden.
Vg
Steffen

Holger war schneller :-)

Dieser Beitrag wurde am 12.06.2019 um 19:53 Uhr von Steffen.111 editiert.
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
003
12.06.2019, 20:56 Uhr
Mario Blunk

Avatar von Mario Blunk

Danke, also Ihr meint dieses ?:

rout_1:
; macht irgendwas mit einem übergebenen Byte
pop hl ; Rücksprungadresse in hl sichern
pop af; übergebenes Byte in Akku laden
push af; Stacktiefe korregieren: irgendwas auf Stack legen
push hl; Rücksprungadresse wiederherstellen

sub a, 4

ret

main:
ld a, 88h
push af
call rout_1
pop af; Stacktiefe korregieren: irgendwas vom Stack schubsen
--
Mein Chef ist ein jüdischer Zimmermann.
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
004
12.06.2019, 21:41 Uhr
maleuma



So etwas ähnliches mach CAOS mit dem CALL PV1. Dort wird der Parameter nach dem CALL als DB angegeben. Im Prinzip so hier:

Quellcode:

CALL PV1
DB UP-Nr.

PV1:    DI
    PUSH    HL  ; HL sichern
    POP    HL  ; HL wieder vom Stack holen
    POP    HL  ; RET-Adresse vom Stack holen
    INC    HL  ; UP-Nummer übergehen
    PUSH    HL  ; als neue RET-Adresse auf Stack legen
    DEC    HL  ; zurück zu UP-Nummer gehen
    DEC    SP  ; Stacktiefe korrigieren
    DEC    SP  ; damit HL wieder geholt werden kann
    EI           ; jetzt stimmt Stack wieder und Interrupt darf wieder kommen
    PUSH    AF
    PUSH    DE
    LD    E,(HL)
    LD    D,0
    LD    HL,(SUTAB)  ; Unterprogrammtabelle
    ADD    HL,DE
    ADD    HL,DE        ; HL=Pos. in Tabelle
    LD    E,(HL)
    INC    HL
    LD    D,(HL)    ; DE = UP-Adresse aus Tabelle
    EX    DE,HL        ; HL = UP-Adresse
    POP    DE
    POP    AF
    EX    (SP),HL    ; HL vom Stack holen und Startadresse ablegen
    RET            ; UP anspringen


Das PV1 verändert dabei kein Register!
--
Mario.
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
005
13.06.2019, 02:07 Uhr
Steffen.111

Avatar von Steffen.111

Hallo Mario,
Dein Beispiel ist richtig nur musst/kannst Du Deine Zeilen "Stacktiefe korr." einfach weglassen. Probiers doch mal mit Skatkarten, dann wirds anschaulich.
Vg
Steffen
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
006
13.06.2019, 03:29 Uhr
Crawler

Avatar von Crawler

Hallo Mario,

deine Routine könnte folgendermaßen ausschauen:


Quellcode:

rout_1:
    pop    hl    ; Rücksprung-Adresse vom Stack holen und in hl merken
    pop    af    ; Parameter vom Stack holen

    sub    4    ; z.B. 4 subtrahieren

    jp    (hl)    ; zurückspringen

main:
    ld    a,88h
    push    af
    call    rout_1    ; Funktionsaufruf mit Stackübergabe
    ...



Der Code ist getestet. Das Register A enthält nach dem Aufruf den richtigen Wert.

Gruß,
Stefan
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
007
13.06.2019, 20:34 Uhr
Crawler

Avatar von Crawler

Steffen hat es bereits richtig erkannt. Dein Beispiel in 003 ist bis auf die beiden Zeilen "Stacktiefe korregieren" korrekt.


Quellcode:

rout_1:
        pop     hl
        pop     af
        push   hl

        sub     4

        ret

main:
        ld      a, 88h
        push    af
        call    rout_1
        ...



Ich hab' das natürlich direkt schon wieder versucht zu optimieren.
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
008
13.06.2019, 21:14 Uhr
Wusel_1



Und warum musst du AF auf dem Stacke ablegen? Noch nichts von "Schattenregister" AF' gehört, also einfach EX AF und um den Wert nach AF wieder zurückzuholen wieder EX AF.
--
Beste Grüße Andreas
______________________________________
DL9UNF ex Y22MF es Y35ZF
JO42VP - DOK: Y43 - LDK: CE

*** wer glaubt hört auf zu denken ***

Dieser Beitrag wurde am 13.06.2019 um 21:15 Uhr von Wusel_1 editiert.
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
009
14.06.2019, 10:56 Uhr
holm

Avatar von holm

Es gibt Computerarchitekturen die den Schattenregistersatz für Interrupts benutzen wollen ohne Dich zu fragen. Einem M80.2x kannst Du damit Löcher in die Leuchtschicht der Bildröhre brennen.

Gruß,

Holm
--
float R,y=1.5,x,r,A,P,B;int u,h=80,n=80,s;main(c,v)int c;char **v;
{s=(c>1?(h=atoi(v[1])):h)*h/2;for(R=6./h;s%h||(y-=R,x=-2),s;4<(P=B*B)+
(r=A*A)|++u==n&&putchar(*(((--s%h)?(u<n?--u%6:6):7)+"World! \n"))&&
(A=B=P=u=r=0,x+=R/2))A=B*2*A+y,B=P+x-r;}
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
010
14.06.2019, 18:42 Uhr
Mario Blunk

Avatar von Mario Blunk


Zitat:
Crawler schrieb

Quellcode:

rout_1:
    pop    hl    ; Rücksprung-Adresse vom Stack holen und in hl merken
    pop    af    ; Parameter vom Stack holen

    sub    4    ; z.B. 4 subtrahieren

    jp    (hl)    ; zurückspringen

main:
    ld    a,88h
    push    af
    call    rout_1    ; Funktionsaufruf mit Stackübergabe
    ...



Der Code ist getestet. Das Register A enthält nach dem Aufruf den richtigen Wert.


Danke. Problem ist aber, daß HL in der Routine nicht geänder werden darf. Bei komplexen Routinen kann das keiner gewährleisten. Der Rücksprung mit jp (hl) ohne ret ist etwas unüblich. In der Bilanz ist der Stack nach call rout_1 wieder korrekt.
--
Mein Chef ist ein jüdischer Zimmermann.
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
011
14.06.2019, 18:47 Uhr
Mario Blunk

Avatar von Mario Blunk


Zitat:
Crawler schrieb

Quellcode:

rout_1:
        pop     hl
        pop     af
        push   hl

        sub     4

        ret

main:
        ld      a, 88h
        push    af
        call    rout_1
        ...




Diese Variante macht den meißten Sinn und ist schlüssig. Fragt sich, ob ein Compiler das auch so macht, wenn das in C geschrieben wird. Ja, Disassembler... vielleicht weiß das auch jemand ?
--
Mein Chef ist ein jüdischer Zimmermann.
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
012
14.06.2019, 18:48 Uhr
Mario Blunk

Avatar von Mario Blunk


Zitat:
Wusel_1 schrieb
Und warum musst du AF auf dem Stacke ablegen? Noch nichts von "Schattenregister" AF' gehört, also einfach EX AF und um den Wert nach AF wieder zurückzuholen wieder EX AF.


Gib mal ein Beispiel durch. Danke.
--
Mein Chef ist ein jüdischer Zimmermann.
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
013
14.06.2019, 18:58 Uhr
holm

Avatar von holm


Zitat:
Mario Blunk schrieb

[..]
Danke. Problem ist aber, daß HL in der Routine nicht geänder werden darf. Bei komplexen Routinen kann das keiner gewährleisten.



Unfug. Neimand hindert Dich HL zwischenzeitlich wieder auf den Stack zu schaffen wenn Du HL benötigst, Du mußt es halt nur vor dem Rücksprung zurück holen.


Zitat:

Der Rücksprung mit jp (hl) ohne ret ist etwas unüblich. In der Bilanz ist der Stack nach call rout_1 wieder korrekt.



Auch Quatsch, jmp (HL) ist alles Andere als selten. Wenn Dir das nicht gefällt dann mach halt push HL, ret, ist exakt das Selbe. Natürlich kannst Du auch ein push HL, pop IX und Anderes verwenden oder die Rückkehradresse gleich nach IX oder IY vom Stack holen, Du mußt nur einen Plan haben was Du machen willst.

Gruß,

Holm
--
float R,y=1.5,x,r,A,P,B;int u,h=80,n=80,s;main(c,v)int c;char **v;
{s=(c>1?(h=atoi(v[1])):h)*h/2;for(R=6./h;s%h||(y-=R,x=-2),s;4<(P=B*B)+
(r=A*A)|++u==n&&putchar(*(((--s%h)?(u<n?--u%6:6):7)+"World! \n"))&&
(A=B=P=u=r=0,x+=R/2))A=B*2*A+y,B=P+x-r;}

Dieser Beitrag wurde am 14.06.2019 um 18:58 Uhr von holm editiert.
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
014
14.06.2019, 19:00 Uhr
holm

Avatar von holm


Zitat:
Mario Blunk schrieb

Zitat:
Wusel_1 schrieb
Und warum musst du AF auf dem Stacke ablegen? Noch nichts von "Schattenregister" AF' gehört, also einfach EX AF und um den Wert nach AF wieder zurückzuholen wieder EX AF.


Gib mal ein Beispiel durch. Danke.



..willst Du uns verarschen oder hast Du einen Denk-Klemmer?

Gruß,

Holm
--
float R,y=1.5,x,r,A,P,B;int u,h=80,n=80,s;main(c,v)int c;char **v;
{s=(c>1?(h=atoi(v[1])):h)*h/2;for(R=6./h;s%h||(y-=R,x=-2),s;4<(P=B*B)+
(r=A*A)|++u==n&&putchar(*(((--s%h)?(u<n?--u%6:6):7)+"World! \n"))&&
(A=B=P=u=r=0,x+=R/2))A=B*2*A+y,B=P+x-r;}
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
015
14.06.2019, 21:38 Uhr
Mario Blunk

Avatar von Mario Blunk


Zitat:
holm schrieb
..willst Du uns verarschen oder hast Du einen Denk-Klemmer?


Ja, ich habe grad einen Denk-Klemmer. Hoffe, das ist keine Schande hier.
--
Mein Chef ist ein jüdischer Zimmermann.
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
016
14.06.2019, 23:01 Uhr
kaiOr

Avatar von kaiOr

?

Quellcode:

rout_1:
    ...
    ex    af, af'
    sub   4
    ret

main:
    ld    a, 88h
    ex    af, af'
    call  rout_1


Den CALL in seine Bestandteile auflösen:

Quellcode:

rout_1:
    ...
    pop   af
    sub   4
    ret

main:
    ld    hl, main1
    push  hl
    ld    a, 88h
    push  af
    jp    rout_1
main1:
    ...


Oder per selbstmodif. Code ein sich wandelnder JP am Ende von Routine 1. Das macht es aber nur undurchsichtiger und setzt voraus, das das Programm im RAM ausgeführt wird.

Ein Byte im RAM reservieren lässt alle anderen Register in Ruh:

Quellcode:

s_AKKU    DEFB 00h

rout_1:
    ...
    ld    a, (s_AKKU)
    sub   4
    ret

main:
    ld    a, 88h
    ld    (s_AKKU), a
    call  rout_1


Dieser Beitrag wurde am 14.06.2019 um 23:19 Uhr von kaiOr editiert.
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
017
14.06.2019, 23:52 Uhr
Crawler

Avatar von Crawler

@008: Ich dachte hier geht es um Stack- und nicht um Registerübergabe!?

@010: Wenn ich schon die Rücksprungadresse in HL habe und der Stack stimmt, kann ich mit JP nach HL springen. Damit spare ich mir ein PUSH und das POP, welches vom RET ausgeführt wird.

@013: Wenn die Index-Register ins Spiel kommen, müssen unter CAOS tatsächlich die Interrupts zwischenzeitlich gesperrt werden.

@Mario: Ich vermute, du möchtest von C aus eine Assembler-Routine aufrufen. Sehe ich das richtig?
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
018
15.06.2019, 09:17 Uhr
Mario Blunk

Avatar von Mario Blunk


Zitat:
Crawler schrieb
@Mario: Ich vermute, du möchtest von C aus eine Assembler-Routine aufrufen. Sehe ich das richtig?


Exakt. Ich hab hier eine Sammlung Assembler-Routinen, die alle möglichen Register verwenden. Um das Ganze zu zähmen, will ich diese Routinen aus einem C Programm heraus aufrufen. Die Frage der Rückgabewerte von Routinen wollte ich hier erstmal noch nicht stellen, käme aber als nächstes.
Ich wühle grad in der SDCC-Doku, wie man das macht. Hab aber nur eine halbe Lösung. Nun bin ich nicht gerade ein Spezialist in Sachen SDCC und C im allgemeinen. Dem In-Line-Assembler des SDCC muß ich irgendwie beibringen, auf welche Absolut-Adressen die besagten Routinen stehen. Ein call rout_1 muß dann zur Adresse 0623h führen. Sowas wie einen globalen Deklarationsblock mit Zeilen wie
i2c_wr_adr equ 0623h
brauche ich in C.
--
Mein Chef ist ein jüdischer Zimmermann.
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
019
16.06.2019, 18:51 Uhr
P.S.



@Mario Blunk <018>
Zu DDR-Zeiten wurde für den U880 häufig in Assembler programmiert.
Dazu gibt es hervorragende DDR-Fachliteratur:
(1) Schwarz, Meyer, Eckhardt: "Mikrorechner", VEB Verlag Technik 1980;
(2) Gerhard Paulin: "Grundlagen der Programmiertechnik", VEB Verlag Technik;
(3) Klaus Kabitzsch: "Mikrorechner in der Automatisierungspraxis", Akademie-Verlag Berlin 1987;
(4) Lampe, Jorke, Wengel: "Algorithmen der Mikrorechentechnik" (U880), VEB Verlag Technik 1984;
(5) Lampe, Jorke, Wengel: "Arithmetische Algorithmen der Mikrorechentechnik", VEB Verlag Technik 1989;
(6) Kieser, Meder: "Mikroprozessortechnik" (U880), VEB Verlag Technik;

Für den Z80 gab's sicherlich im Westen auch so was ähnliches. Ich hatte mal Ende der 1980er einige handschriftliche Auszüge aus einem Franzis-Fachbuch machen können - die sind nun aber samt der Hardware in Erfurt.

Das Wissen der Menschheit gehört allen Menschen! -
Wissen ist Macht - wer nur glaubt, der weiß gar nichts! -
Jedoch - Unwissenheit schützt vor Strafe nicht! -
Gegen die Ausgrenzung von Unwissenden und für ein liberalisiertes Urheberrecht!
PS

Dieser Beitrag wurde am 16.06.2019 um 18:54 Uhr von P.S. editiert.
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
020
16.06.2019, 21:05 Uhr
RenéB



Das unter (5) angegebene Buch beschreibt zwar die Parameterübergabe über einen Stapel, aber nicht über den Stack. Da wird HL als Zeiger zu den Parametern verwendet.
Das ist also nicht das was Mario wissen wollte - denke ich.
Gruß René
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
021
16.06.2019, 23:24 Uhr
Crawler

Avatar von Crawler


Zitat:
Mario Blunk schrieb
Ich hab hier eine Sammlung Assembler-Routinen, die alle möglichen Register verwenden. Um das Ganze zu zähmen, will ich diese Routinen aus einem C Programm heraus aufrufen.



Das verstehe ich jetzt nicht. Wo ist das Problem mit den Routinen? Was willst du erreichen?

Um Registerinhalte für das aufrufende Programm zu bewahren, können die Befehle push und pop verwendet werden. Was versprichst du dir von dem Aufruf aus einem C-Programm heraus?

In der SDCC-Doku http://sdcc.sourceforge.net/doc/sdccman.pdf ist unter 4.3.3 Calling conventions beschrieben, wie die Parameterübergabe normalerweise funktioniert:

Die Parameter werden, in der Reihenfolge, wie in der Parameterliste angegeben, von rechts nach links auf den Stack abgelegt. Returnwerte werden in einem Register zurückgegeben (8bit-Werte in L, 16bit-Werte in HL und 32bit-Werte in DEHL). Das Auflösen der Adressen kann der Linker übernehmen, wenn die Assembler-Routinen als Objekt-Datei vorliegen. Ansonsten könntest du sie auch gleich komplett in Form von Inline-Assembler einbinden.

Dieser Beitrag wurde am 16.06.2019 um 23:29 Uhr von Crawler editiert.
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
022
17.06.2019, 14:28 Uhr
Bert




Zitat:
Mario Blunk schrieb
...ein sehr einfaches Beispiel, wie über den Stack ein Parameter an eine Routine übergeben wird?



Beim SDCC wird das z.B. so gemacht:

Quellcode:

; some functions provided by the CAOS (=monitor program)

; first parameter LSB on SP+2
; first parameter MSB on SP+3
; 2nd   parameter LSB on SP+4
; 2nd   parameter MSB on SP+5
; return values on L or HL

    .include 'caos.inc'


; Initialisieren mehrerer E/A-Kaenale ueber Tabelle(n)
; in:
;  Register HL - Anfangsadresse der Tabelle
;  Register  D - Anzahl der Kanaele
.globl _inime
_inime::
    ld hl,#2
    add hl, sp
    ld    e, (hl)
    inc hl
    ld    d, (hl)
    inc hl
    ld    a, (hl)
    ex de, hl
    ld  d, a
    call PV1
    .db FNINIME
ret


Der aufrufende Programmteil weiß ja, wieviele Parameter auf dem Stack liegen...

Grüße,
Bert
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
023
17.06.2019, 20:50 Uhr
Crawler

Avatar von Crawler

Das heißt, der vom SDCC Compiler für den Aufruf generierte Code räumt den Stack nachher selbst wieder ab. Das vereinfacht die Sache allerdings.
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
024
18.06.2019, 09:56 Uhr
Hobi



Eine andere, für den Assemblerprogrammierer sehr einfache Methode, ist die Fastcall Konvention.

Wird die Funktion so deklariert, erfolgt die Parameterübergabe im Register.
Uint16 foo(uint16 arg) __z88dk_fastcall;

Oder
Uint16 foo(uint16 arg) __z88dk_callee;
Hier kümmert sich die Funktion um den Stack.

Pop iy; return addres
Pop bc; arg
Push iy
Ret

Wahlweise würde wohl auch jmp (iy) funktionieren

Beispiele zum ADAC gibt es hier
https://github.com/anchorz/sdcc-z1013-kc85/blob/master/sample_02_compiler/src/main.c
--
-------------------------------------------
Winterschlaf
-------------------------------------------

Dieser Beitrag wurde am 18.06.2019 um 09:59 Uhr von Hobi editiert.
Seitenanfang Seitenende
Profil || Private Nachricht || Suche Zitatantwort || Editieren || Löschen
Seiten: -1-     [ Technische Diskussionen ]  



Robotrontechnik-Forum

powered by ThWboard 3 Beta 2.84-php5
© by Paul Baecher & Felix Gonschorek