News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Stack Cleanup

Started by tda0626, May 31, 2024, 08:55:18 AM

Previous topic - Next topic

tda0626

If I have a function like:

PlotLine Proc x1:word, y1:word, x2:word, y2:word, color:byte

Would I use ret 9 to clean up the stack from the 9 bytes pushed onto it or do I need to add 9 to BP?

Tim

NoCforMe

Yikes, not 9!
Don't push bytes on a word-aligned stack; make that parameter a WORD instead (and extract the byte from it inside the routine). The correct # would then be 10.
Assembly language programming should be fun. That's why I do it.

tda0626

Thanks! I will change it to words.

Forgot about this!

Stack   SEGMENT STACK
   DW 256 DUP(?)
Stack   ENDS


Tim


NoCforMe

BTW, RET n adjusts SP (ESP), not BP (EBP). (E)BP is no longer meaningful after a return.
Assembly language programming should be fun. That's why I do it.

tda0626

Do I do something like this before the ret 10 to restore BP?

mov sp, bp
pop bp



sinsi

If you are using PROC and INVOKE, just let ML take care of SP and BP.
Just set your .model to C or STDCALL

tda0626

Thanks! I actually have stdcall defined already.


Tim

_japheth


Actually, "RET" inside a PROC is an internal macro and if used without argument will trigger epilogue creation. That means, it

- removes local variables by restoring E/SP
- pop registers listed in the "USES" clause
- restores E/BP
- emits the suitable variant of the "ret" instruction (near/far, with or without argument).

Btw, there's no problem with arguments of type BYTE. The assembler will always ensure that the stack is aligned ( word-aligned in 16-bit, dword-aligned in 32-bit ). That means, a BYTE argument in 32-bit will make the assembler push the byte as the "lowest" byte of a dword.
Dummheit, gepaart mit Dreistigkeit - eine furchtbare Macht.

sinsi

Quote from: _japheth on June 01, 2024, 06:03:56 PMActually, "RET" inside a PROC is an internal macro and if used without argument will trigger epilogue creation.
RET isn't an internal macro but it does trigger the epilogue macro.
The FLAGS parameter lets the epilogue macro know what type of RET it is, RET or RET n or IRET

Here is the PROLOGUE.INC from the MASM 6 floppy. It's 16-bit but the same basic idea is used for 32-bit.
pushcontext listing
.nolist
;
;       (C) Copyright Microsoft Corporation 1992

;       This file is used to create the same sets of prologue and epilogue
;       sequences which the Microsoft C 6.00 compiler will produce.  This
;       file would be used for writing windows programs and to provide
;       such features as stack checking in the assembler portions of
;       a C based project.


;       The following global variables will affect the prolog/epilog
;       sequences produced
;
;       ?WP_DEBUG - If 1 then prolog/epilog sequences will be forced
;       ?WP_CHECKSTACK - If 1 then a check stack will be forced on all
;                       procedures
;       ?WP_INCBP - If 1 then the inc bp sequence will be generated on
;               all far procedures
;       ?WP_LOADDS - If 1 then the load ds sequence will be generated on
;               all far procedures
;
ifndef ?WP_DEBUG
?WP_DEBUG = 0
endif
ifndef ?WP_CHECKSTACK
?WP_CHECKSTACK = 0
endif
ifndef ?WP_INCBP
?WP_INCBP = 0
endif
ifndef ?WP_LOADDS
?WP_LOADDS = 0
endif

;
;       Complain if we are in a segment as this will affect how the
;       externdefs are done and therefore the fixups and code
;       created on the checkstack calls
;
% ifnb   <@CurSeg>
echo    Include should not be contained in a segment
endif

externdef C     _aNchkstk:near          ; Extern the symbols
externdef C     _aFchkstk:far           ; for later reference

;
;       This macro will produce the same output as will the
;       C6 compiler for the given switches.
;
;       The following may be placed in the MacroArgs field of the
;               proc defintion:
;
;       CHECKSTACK
;       NOCHECKSTACK
;       LOADDS
;       NOLOADDS
;       FORCEFRAME
;       INCBP
;       NOINCBP

option prologue:cPrologue

cPrologue macro szProcName, flags, cbParams, cbLocals, rgRegs, rgUserParams
    LOCAL   ?doPrologue
    LOCAL   ?loadds
    LOCAL   ?checkstack
    LOCAL   ?incbp
    LOCAL   ?cbLocals
;       pushcontext     listing
;       .nolistmacro
;       .listmacroall
   
    ?doPrologue = 0
    ?loadds = 0
    ?checkstack = 0
    ?incbp = 0
    ?cbLocals = cbLocals

;;      Set the defaults based on the global values specified
;;
if      ?WP_DEBUG NE 0                  ;; Force frames by default
 ?doPrologue = 1
endif

if      ?WP_CHECKSTACK NE 0             ;; Force checkstack by default
 ?checkstack = 1
endif

if      ?WP_INCBP NE 0                  ;; Force incbp by default if far
 if flags AND 020h
  ?incbp = 1
 endif
endif

if      ?WP_LOADDS NE 0                 ;; Force loadds by default if far
 if flags AND 020h
  ?loadds = 1
 endif
endif

;;
;;      Get all of the user parameters parsed
;;

ifnb    <rgUserParams>          ;;      Parse user params if exsisting
 for p,<rgUserParams>           ;;      For every user param

  ifidn <p>, <CHECKSTACK>       ;;      Is it checkstack?
   ?checkstack = 1              ;;              Yes -- do checkstack
  endif

  ifidn <p>, <NOCHECKSTACK>     ;;      Don't do checkstack?
   ?checkstack = 0              ;;              Yes -- clear checkstack
  endif

  ifidn <p>, <LOADDS>   ;;      Is it LoadDS
   ?loadds = 1                  ;;              Yes -- do loadds sequence
  endif

  ifidn <p>, <NOLOADDS> ;;      Don't do LoadDS?
   ?loadds = 0                  ;;              Yes -- clear loadds flag
  endif

  ifidn <p>, <INCBP>            ;; Is it IncBP
   if flags AND 020h            ;;      and far?       
    ?incbp = 1                  ;;      Yes -- do IncBP sequence
   endif
  endif

  ifidn <p>, <NOINCBP>  ;;      Is it NoIncBP
   ?incbp = 0                   ;;              Yes -- Clear the incbp flag
  endif

  ifidn <p>, <FORCEFRAME>       ;;      Is it ForceFrame?
   ?doPrologue = 1                      ;;              Yes -- force out a frame
  endif

 endm                   ;; End of user parameter parsing loop
endif

;;  Frames are generated iff
;;      1. cbLocals + cbParams != 0
;;      2. FORCEFRAME is set
;;      3. INCBP is set and proc is far
;;      4. LOADDS is set
;;
;; Force a prolog?     

?doPrologue = ?doPrologue OR ?incbp OR ?loadds OR ?checkstack OR (?cbLocals NE 0) OR (cbParams NE 0)

if      ?doPrologue EQ 0        ;; No prolog needed -- so get out of here
;       popcontext listing
    exitm<0>
endif

if      ?loadds EQ 1            ;; Create the loadds code -- force in
    push    ds              ;;      Put DS into AX -- we will place
    pop     ax              ;;      back in DS later.  This sequence
    nop                     ;;      is altered by the OS if needed
endif

if      ?incbp EQ 1             ;; Mark as a far procedure for stack
    inc     bp              ;;      walking
endif

    push    bp              ;; Create the frame
    mov     bp,sp

if      ?loadds EQ 1            ;; Load up DS with the value in AX
    push    ds              ;;
    mov     ds,ax           ;;
    ?cbLocals = ?cbLocals + 2
endif

if      ?checkstack EQ 1        ;; Now  allocate space for locals
    mov     ax,cbLocals     ;;      # of bytes of locals (unadjusted)
% ifidn <@CurSeg>, <_TEXT>
    call    _aNchkstk       ;;      Call run time routine to allocate
 else
    call    _aFchkstk
 endif
else    ; ?checkstack NE 1
  if    cbLocals NE 0
    sub     sp,cbLocals     ;;      make space on the stack for locals
  endif
endif

ifnb    rgRegs                  ;; There are registers to be saved.  do so
    for r,rgRegs
        push    r
    endm
endif
;       popcontext listing
    exitm <?cbLocals>

endm



;
;       This macro will produce the same output as will the
;       C6 compiler for the given switches.
;
;       The following may be placed in the MacroArgs field of the
;               proc defintion:
;
;       CHECKSTACK
;       NOCHECKSTACK
;       LOADDS
;       NOLOADDS
;       FORCEFRAME
;       INCBP
;       NOINCBP

option epilogue:cEpilogue

cEpilogue macro szProcName, flags, cbParams, cbLocals, rgRegs, rgUserParams
    LOCAL   ?doPrologue
    LOCAL   ?loadds
    LOCAL   ?checkstack
    LOCAL   ?incbp
;       pushcontext     listing
;       .nolistmacro
;       .listmacroall
   
    ?doPrologue = 0
    ?loadds = 0
    ?checkstack = 0
    ?incbp = 0

;;      Set the defaults based on the global values specified
;;
if      ?WP_DEBUG NE 0                  ;; Force frames by default
 ?doPrologue = 1
endif

if      ?WP_CHECKSTACK NE 0             ;; Force checkstack by default
 ?checkstack = 1
endif

if      ?WP_INCBP NE 0                  ;; Force incbp by default
 if flags AND 020h
  ?incbp = 1
 endif
endif

if      ?WP_LOADDS NE 0                 ;; Force loadds by default
 if flags AND 020h
  ?loadds = 1
 endif
endif

;;
;;      Get all of the user parameters parsed
;;

ifnb    <rgUserParams>          ;;      Parse user params if exsisting
 for p,<rgUserParams>           ;;      For every user param

  ifidn <p>, <CHECKSTACK>       ;;      Is it checkstack?
   ?checkstack = 1              ;;              Yes -- do checkstack
  endif

  ifidn <p>, <NOCHECKSTACK>     ;;      Don't do checkstack?
   ?checkstack = 0              ;;              Yes -- clear checkstack
  endif

  ifidn <p>, <LOADDS>   ;;      Is it LoadDS
   ?loadds = 1                  ;;              Yes -- do loadds sequence
  endif

  ifidn <p>, <NOLOADDS> ;;      Don't do LoadDS?
   ?loadds = 0                  ;;              Yes -- clear loadds flag
  endif

  ifidn <p>, <INCBP>    ;;      Is it IncBP
   if flags AND 020h
    ?incbp = 1                  ;;              Yes -- do IncBP sequence
   endif
  endif

  ifidn <p>, <NOINCBP>  ;;      Is it NoIncBP
   ?incbp = 0                   ;;              Yes -- Clear the incbp flag
  endif

  ifidn <p>, <FORCEFRAME>       ;;      Is it ForceFrame?
   ?doPrologue = 1                      ;;              Yes -- force out a frame
  endif

 endm                   ;; End of user parameter parsing loop
endif

;;  Frames are generated iff
;;      1. cbLocals + cbParams != 0
;;      2. FORCEFRAME is set
;;      3. INCBP is set and proc is far
;;      4. LOADDS is set
;;
;; Force a prolog?     

?doPrologue = ?doPrologue OR ?incbp OR ?loadds OR ?checkstack OR (cbLocals NE 0) OR (cbParams NE 0)

if      ?doPrologue EQ 0        ;; No epilog needed -- so get out of here
    ret
    exitm
endif

ifnb    rgRegs                  ;; Pop off the registers -- they are in
    for r,rgRegs            ;; inverse order from the prologue call
        pop     r
    endm
endif

if      ?loadds                 ;;
    dec     bp
    dec     bp
    mov     sp,bp
    pop     ds
    pop     bp
else

    mov     sp,bp
    pop     bp
endif

if      ?incbp                  ;; Remove the increment of BP if necessary
    dec     bp
endif

if      flags AND 010h          ;; Caller pops stack arguments
    ret
else                            ;; Callee pops args
 if     cbParams NE 0           ;; Put out the correct form of return
    ret     cbParams
 else
    ret
 endif
endif
endm

popcontext      listing
.listmacro


NoCforMe

Quote from: _japheth on June 01, 2024, 06:03:56 PMBtw, there's no problem with arguments of type BYTE. The assembler will always ensure that the stack is aligned ( word-aligned in 16-bit, dword-aligned in 32-bit ).
Good to know.
Still, I wouldn't use anything except "proper" stack parameters (WORD in 16-bit, DWORD in 32-bit) even so. Putting bytes on the stack just feels ... icky and wrong.
Assembly language programming should be fun. That's why I do it.

tda0626

Quote from: _japheth on June 01, 2024, 06:03:56 PM- emits the suitable variant of the "ret" instruction (near/far, with or without argument).




So you are saying I don't have to specify a value with ret because the ML will do that for me?

NoCforMe

Yes, exactly.
It knows how much stuff was pushed on the stack.
Assembly language programming should be fun. That's why I do it.

tda0626

Awesome, I am glad I asked this question. Thanks for the answers.