Prozeduren abkapseln



  • Hi!
    Vorweg, ich benutze PMAS Assembler, somit kann ich Assemblerdirektiven nicht gebrauchen.

    Es geht darum, ich will Übergabeparameter im Stack speichern. Die Prozedur soll dann beim Aufruf alle verwendetetn Register ebenfalls auf den Stack sichern. Somit kapsel ich die Prozedur komplett vom Hauptprogramm ab. Jedoch sieht der Stack ja danach so aus:

    Reg C
    Reg B
    Reg A
    Para 3
    Para 2
    Para 1
    Rücksprungadresse

    In der Prozedur muss ich ja dann irgendwie die Übergabeparameter auswerten. Somit müsste ich sie auch vom Stack herunternehmen. Das geht jedoch nicht, da die 3 Register oben drauf liegen. Diese will ich aber erst nach Beenden der Prozedur wieder zurückschreiben und somit auch herunternehmen.

    Wie macht ihr bzw. eure Assembler das? Die Register der 8 Bit CPU sind beschrenkt, deswegen kann ich kein Tempregister für soetwas definieren.

    Die Register vor Aufruf zu sichern wäre eine Möglichkeit, ist jedoch schwerer dann anzusprechen.

    Ist das ein unmögliches Uterfangen?

    mfg olli


  • Mod

    wenn die cpu keine möglichkeit hat, entweder indirekt über ein register, oder relativ zum stackpointer zu adressieren, wirds schwierig, eigentlich sollte wenigstens eins von beidem immer möglich sein. kenne selbst nur 8080/85 bzw.Z80 assembler, und da ist das kein problem, bestimmte registerpaare lassen sich hier zum adressieren nutzen.

    sonst würde ja im prinzip nichts anderes übrig bleiben, als interrupts zu deaktivieren, und dann den stackpointer direkt zu manipulieren.



  • Danke! Hmm wie ich gerade festgestellt habe würde der Stack ja so aussehen:
    Reg C
    Reg B
    Reg A
    Rücksprungadresse
    Para 3
    Para 2
    Para 1

    Auf die Registersicherung hätte ich ja noch verzichten können, aber auf die Parameter? Hmmm. Den SP direkt zu manipulieren, das geht, ist zwar nicht die feine Art, aber besser als garnix. ADD SP, 3 (3 Byte Rücksprungadresse) ist also die Lösung. Bin jedoch für "elegantere" Vorschläge noch offen 🙂
    mfg olli


  • Mod

    laut opcode liste verfügt die cpu über befehle der art movb r, [SP+ofs8] - du kannst also direkt relativ zum stackpointer adressieren (-128 ... +127)



  • Ja, aber movw rw, [sp+ofs8] rw = 16 Bit Register. Somit könnte ich nie auf einzelne Bytewerte zugreifen. Weiter fällt mir ein, dass in der Prozedur bei lokalen Variablen es zum Überschreiben der Rücksprungadresse kommen kann. Somit müsste ich irgendwie die Rücksprungadresse ans Ende des Stacks knallen. Also noch vor dem Call den Stackpointer manipulieren. Arg das ist vllt. ne Scheiße.
    mfg olli



  • so wird es ja auch gemacht, man pusht zuerst die parameter und dann ruft man die prozedur auf.
    danach greift man auf die parameter mit dem stackpointer zu, deshalb ist es wichtig, dass sowas wie [sp+2] geht. wenn du lokale parameter benutzen willst dann brauchst du noch ein zweites register, das du zum adressieren benutzen kannst.
    dann bringst du den inhalt von sp in dieses register, kannst es auch vorher auf dem stack sichern und dann benutzt du dieses um auf die parameter zuzugreifen. dann subtrahierst du von sp die anzahl an bytes, die du für deine lokalen variablen brauchst und adressierst diese über sp.


  • Mod

    da man für gewöhnlich (bis auf ein paar spezielle ausnahmen) weiss, wie gross der stackrahmen (also die anzahl der lokal gepushten bytes) and einer bestimmten stelle des codes ist, kann man sich normalerweise sparen, SP erst in einem anderen register zu sichern, und es direkt zum adressieren nutzen.



  • http://devgba.snowcristals.net/index.php/PM:Assembler_Tutorial#Prozeduren (noch nicht fertig geschrieben) ich mache es nun so, wie ich hier beschrieben habe. Ist denke das ist die effektivste Methode 🙄
    mfg olli


  • Mod

    wie gesagt, das scheint umständlicher zu sein als es sein müsste. ein aufruf im stile von _cdecl sollte problemlos möglich sein:
    im sinne von _cdecl void kopiere(void* src, void* dst, size_t len) allerdings mit parameterübergabe von links nach rechts (ist im prinzip nach belieben, nur muss man nat. konsequent sein)

    main:
    	    mov x1, garfield ; Quelladresse
    	    push x1
    	    mov x1, 0x001000 ; Zieladresse
    	    push x1
    	    mov x1, 96*64/8  ; Länge hier 786 Byte
    	    push x1
    	    call kopiere     ; Prozedur nun aufrufen
            add SP, 6        ; Stack aufräumen
    	; lcd konfiguration 0
    	mov x1, 0x002080
    	mov [x1], 0b00001000 ; Enable 0 + Linearmodus
    
    	; lcd konfiguration 1
    	mov x1, 0x002081
    	mov [x1], 0b00001001 ; Enable 1
    
    	; Programm beenden
    	jmp ende
    
    kopiere:
            movw x1, [SP+7]
            movw x2, [SP+5]
            movw hl, [SP+3]
    kopiere_schleife:
    	    mov a, [x1] ; kopiere Byte von Quelladresse in A
    	    mov [x2], a ; kopiere A in Zieladresse
            inc x1
            inc x2
            dec hl    ; beinflusst keine flags?
            cmp hl, 0 ; dann brauchen wir noch das hier
    	    jnz kopiere_schleife
            ret
    

    ich kann das nat. nicht testen. die interaktion zw. 24bit registern und 16bit stackwerten ist mir auch ein bisschen unklar, könnte also sein, dass hier noch irgendwelche offsets zu verändertn sind.

    wenn du mit byte werten arbeiten willst, kannst du sie ja zu 16bit erweitern und dann übergeben - oder du sicherst ein anderes register (z.b. x1) und benutzt es, um den stackrahmen zu adressieren (sozusagen das, was BP beim x86 ist):

    prozedur:
            push x1
            mov  x1, sp
            mov a, [x1+5]   ; erster parameter...
            .
            .
            .
            pop  x1
            ret
    


  • Es wäre echt die konfortabelste Lösung, aber der Debugger gibt mir nur "???" bei movw x1, [sp+1] aus. Ich müsste das nochmal mit einen anderen Debugger testen, aber vorerst geht es leider nicht.
    mfg olli

    Edit: Ja, der Minimon Debugger(ist die neuste Version) zeigt auch nur ???? an. Ziemlich schade, da hier durch das unnötige Ikrementieren des SPs bei POP wertvolle Zeit verschenkt wird.


Anmelden zum Antworten