flat assembler
Message board for the users of flat assembler.

Index > Macroinstructions > solved:define name subnames (like structure)at preprocessing

Author
Thread Post new topic Reply to topic
ProMiNick



Joined: 24 Mar 2012
Posts: 805
Location: Russian Federation, Sochi
ProMiNick 03 Feb 2023, 09:35
Code:
macro structurise target,source,dummy,[field,type,def] {
      match any,field \{
         match any,target#.fields \\{ target#.fields equ target#.fields , \\} ; 2 lines for later restoring symbols
         target#.fields equ target#.fields target#.#field                     ;
         target#.#field equ target + source#.#field
         target#.#field struct type \} ; how pass here target#.#field itself, but not value it equaled to
      match ,field \{ structurise target,source,def\} } ;is it need some wrapping that "def" lied on "dummy,[field,type,def]" params

struc struct name { .: match any =, more,fields@#name \{ structurise .,name,, fields@#name \} }    


Tomasz, can thou help? thanks.
what is needed:
build set of preprocessor equations based on walking throw every member of fields@#type for any type declared with struct macro
every member had three item field,type,def
if member named - "match any,field" - build equation and try walk via its fields@#type if any
if member unnnamed - substruct case - recurse algorithm, substruct has same members field,type,def but in front of first one of them is dummy parameter

Code:
struct RECT2
       a RECT
       union
        b RECT
        struct
         c RECT
         d RECT
        ends
       ends
ends    
Code:
fields@RECT2:
        field              type              def
        a,                RECT,             <<>,<>,<>,<>> ; field not empty - make equation assignment, check if type declared via struct macro(fields@#type defined by value that holds at least one comma symbol), walk fields@#type if any
                             ,
        ,                 subunion,         <,b,RECT,<<>,<>,<>,<>>,,substruct,<,c,RECT,<<>,<>,<>,<>>,d,RECT,<<>,<>,<>,<>>>> ;field empty - substruct case - recurse walk, but in opposition to fields@#type walk subunion.def walk substruct started from dummy param and than goes members (field type def)
                                            <
                                             ;dummy param
                                             ,
                                             substruct.field    substruct.type  substruct.def
                                             b,                 RECT,           <<>,<>,<>,<>>
                                                                  ,
                                             ,substruct,<,c,RECT,<<>,<>,<>,<>>,d,RECT,<<>,<>,<>,<>>>> ; again substruct    

_________________
I don`t like to refer by "you" to one person.
My soul requires acronim "thou" instead.


Last edited by ProMiNick on 04 Feb 2023, 21:31; edited 1 time in total
Post 03 Feb 2023, 09:35
View user's profile Send private message Send e-mail Reply with quote
Tomasz Grysztar



Joined: 16 Jun 2003
Posts: 8359
Location: Kraków, Poland
Tomasz Grysztar 03 Feb 2023, 18:04
Coming from fasmg+CALM it's not easy to go back to constructing complex fasm 1 macros. Perhaps you could reduce this to a set of smaller problems that would be easier to wrap one's head around?
Post 03 Feb 2023, 18:04
View user's profile Send private message Visit poster's website Reply with quote
ProMiNick



Joined: 24 Mar 2012
Posts: 805
Location: Russian Federation, Sochi
ProMiNick 03 Feb 2023, 21:46
Thanks anyway.
Looks like I found solution:
setup environment for macro & test
Code:
use32
include 'win32a.inc'    

helper macros hold whole logic
Code:
macro recurse@structurise {
        macro structurise target,source,dummy,[field,type,def] \{
                match any,field \\{
                   match any,target\#.fields \\\{ target\#.fields equ target\#.fields , \\\} ; 2 lines for later restoring symbols
                   target\#.fields equ target\#.fields target\#.\#field                     ;
                   target\#.\#field equ target + source\#.\#field
                   define try target\#.\#field
                   match _field,try \\\{
                   recurse@substruct
                   _field substruct type
                   restruc substruct \\\}
                   \\}
                match ,field \\{
                recurse@structurise
                structurise target,source,def
                purge structurise
        \\} \} }
recurse@structurise

macro recurse@substruct {
        struc substruct name \{
                match any =, more,fields@\#name \\{
                        match fields,fields@\#name \\\{
                                recurse@structurise
                                structurise .,name,, fields
                                purge structurise             \\\} \\} \} }
recurse@substruct     

just syntax decoration of helper macros
Code:
struc struct name {
.: .#.fields equ
. substruct name }    


test piece
Code:
struct RECT2
       a RECT
       union
        b RECT
        struct
         c RECT
         d RECT
        ends
       ends
ends

rectR struct RECT2
 mov eax, [rectR]
 mov eax, [rectR.a]
 mov eax, [rectR.a.left]
 mov eax, [rectR.a.top]
 mov eax, [rectR.a.right]
 mov eax, [rectR.a.bottom]
 mov eax, [rectR.b]
 mov eax, [rectR.b.left]
 mov eax, [rectR.b.top]
 mov eax, [rectR.b.right]
 mov eax, [rectR.b.bottom]
 mov eax, [rectR.c]
 mov eax, [rectR.c.left]
 mov eax, [rectR.c.top]
 mov eax, [rectR.c.right]
 mov eax, [rectR.c.bottom]
 mov eax, [rectR.d]
 mov eax, [rectR.d.left]
 mov eax, [rectR.d.top]
 mov eax, [rectR.d.right]
 mov eax, [rectR.d.bottom]    


successfull compilation mean:
1. all structuring attached to symbol rectR,
2. all members of rectR assigned size too from struct members
futher test:
Code:
irp all, rectR.fields { match any,all \{restore any \} }
 mov eax, [rectR]
 rectR.a = 0
 mov eax, [rectR.a]
 mov eax, [rectR.a.left]
 mov eax, [rectR.a.top]
 mov eax, [rectR.a.right]
 mov eax, [rectR.a.bottom]
 mov eax, [rectR.b] ; Error: undefined symbol 'rectR.b'.
 mov eax, [rectR.b.left]     

succesfull crash mean:
1. members are preprocessor symbols, not labels and could be redefined
2. bad - member childs not in rectR.fields because of no error at mov eax, [rectR.a.left]

So left 5 smaller problem -
1. Is it realy work, or I just got luck and some structs could crash my algo? (need shurance)
2. Child members of substructures in code logic passed to rectR.fields, but something goes wrong and they not passed. (need help)
3. May be something in logic could be reduced or cutted off without suffer pof logic (need advise)
4. of cource all compiled, but is values compiled correctly?
Is "target\#.\#field equ target + source\#.\#field" was right solution or I should use define instead of equ? (need advise)
5. in test example rectR defined is label, but in future it supposed to be movable variable, that in other pieces of code have different values and struct members should follow move of rectR (related to problem 4)

Main reason of constructing of all of it - moving variable with whole structure of child fields names.

by the way I hope
Code:
rectR struct RECT2    
is faster than
Code:
virtual
 rectR RECT2
end virtual; it is far from abilities of previous one, but is most similar by action for case where is not supposed to move structure    

_________________
I don`t like to refer by "you" to one person.
My soul requires acronim "thou" instead.
Post 03 Feb 2023, 21:46
View user's profile Send private message Send e-mail Reply with quote
ProMiNick



Joined: 24 Mar 2012
Posts: 805
Location: Russian Federation, Sochi
ProMiNick 04 Feb 2023, 21:46
helper macros modification:
Code:
macro recurse@structurise {
        macro structurise target,source,dummy,[field,type,def] \{
                match any,field \\{
                   match any,target\#.fields \\\{ target\#.fields equ target\#.fields , \\\} 
                   target\#.fields equ target\#.fields target\#.\#field                     
                   target\#.\#field equ target + source\#.\#field
                   define try target\#.\#field
                   match _field,try \\\{
                   recurse@substruct
                   _field substruct type
                   match any =, rest, _field\\\#.fields \\\\{ target\#.fields equ target\#.fields , _field\\\#.fields \\\\} ; this line added it collects subfields
                   restruc substruct \\\}
                   \\}
                match ,field \\{
                recurse@structurise
                structurise target,source,def
                purge structurise
        \\} \} }     

now futher test crash in proper place:
Code:
irp all, rectR.fields { match any,all \{restore any \} }
 mov eax, [rectR]
 rectR.a = 0
 mov eax, [rectR.a]
 mov eax, [rectR.a.left] ; Error: undefined symbol 'rectR.a.left'.
 mov eax, [rectR.a.top]
 mov eax, [rectR.a.right]
 mov eax, [rectR.a.bottom]
 mov eax, [rectR.b] 
 mov eax, [rectR.b.left]    

succesfull crash mean:
1. members are preprocessor symbols, not labels and could be redefined
2. all preprocessor symbols that created by "rectR struct RECT2" are cleared with "irp all, rectR.fields { match any,all \{restore any \} }"

So left 5 smaller problem -
...
[Solved] 2. Child members of substructures in code logic passed to rectR.fields, but something goes wrong and they not passed.
...
Post 04 Feb 2023, 21:46
View user's profile Send private message Send e-mail Reply with quote
ProMiNick



Joined: 24 Mar 2012
Posts: 805
Location: Russian Federation, Sochi
ProMiNick 06 Feb 2023, 23:32
i called unit STRUCTEX.INC
Code:
; Macroinstructions for structurised naming without defining of data

macro recurse@structurise {
        macro structurise target,source,dummy,[field,type,def] \{
                match any,field \\{
                   match any,target\#.fields \\\{ target\#.fields equ target\#.fields , \\\} 
                   target\#.fields equ target\#.fields target\#.\#field                     
                   target\#.\#field equ target + source\#.\#field
                   define try target\#.\#field
                   match _field,try \\\{
                   recurse@substruct
                   _field substruct type
                   match any =, rest, _field\\\#.fields \\\\{ target\#.fields equ target\#.fields , _field\\\#.fields \\\\} ; this line added it collects subfields
                   restruc substruct \\\}
                   \\}
                match ,field \\{
                recurse@structurise
                structurise target,source,def
                purge structurise
        \\} \} } 
recurse@structurise

macro recurse@substruct {
        struc substruct name \{
                match any =, more,fields@\#name \\{
                        match fields,fields@\#name \\\{
                                recurse@structurise
                                structurise .,name,, fields
                                purge structurise             \\\} \\} \} }
recurse@substruct    


than test it on procedure like definition(named STACK.INC):
Code:
use32
format binary as 'dll' ; just for convinience, I have disassembler is in the shell for dll files to see output
include 'struct.inc'
include 'structex.inc'

varbase equ esp ; tested only principles, so varbase so simple

macro stack@arg arg {
        local @proxy,@depth
        @depth = stack@depth
        match name rest,arg: \{
                name equ varbase+@depth
                define @proxy name
                stack@depth = stack@depth+4
                \}
        match name:type,arg \{
                match _name,@proxy \\{
                        _name substruct type
                        match any =, rest, _name\\#.fields \\\{ _locals equ _locals , _name\\#.fields \\\} \\}
                if defined sizeof.\#type
                        stack@depth = stack@depth+sizeof.\#type-4
                end if \} }

macro RTL_C [arg] {
 common
        ret@address equ varbase
        stack@depth = 4
 forward
        stack@arg arg }

macro LTR_pascal [arg] {
 common
        ret@address equ varbase
        stack@depth = 4
 reverse
        stack@arg arg }

struct RECT
  left   dd ?
  top    dd ?
  right  dd ?
  bottom dd ?
ends


hello: LTR_pascal a:RECT,b,c,d
        mov eax,[a.top]
        mov eax,[b]
        mov eax,[c]
        mov eax,[d]    

than test it on locals like definition (named LOCALS.INC):
Code:
use32
format binary as 'dll'
include 'struct.inc'
include 'structex.inc'

varbase equ esp

macro recurse@localunion {
        macro localunion depth,dummy,[field,type,def] \{
         \common \local @depth
                @depth = depth
         \forward
                match any,field \\{
                        virtual at 0
                                type def
                                if @depth+$ > stack@depth
                                        stack@depth = @depth+$
                                end if
                        end virtual
                        _locals equ _locals , field
                        field equ varbase+@depth
                        define try field
                        match _field,try \\\{
                                _field substruct type
                                match any =, rest, _field\\\#.fields \\\\{
                                        _locals equ _locals , _field\\\#.fields \\\\} \\\} \\}
                match ,field \\{
                        recurse@localunion
                        localunion @depth,def
                        purge localunion \\} \} }
recurse@localunion


macro localroot dummy,[field,type,def] {
        local @depth
        @depth = stack@depth
        match any,field \{
                virtual at 0
                        type def
                        stack@depth = @depth+$
                end virtual
                _locals equ _locals , field
                field equ varbase+@depth
                define try field
                match _field,try \\{
                        _field substruct type
                        match any =, rest, _field\\#.fields \\\{
                                _locals equ _locals , _field\\#.fields \\\} \\} \}
        match ,field \{
                recurse@localunion
                localunion @depth,def
                purge localunion \} }

macro locals {
        macro union \{
                macro endu \\{
                        purge endu,ends
                        field@struct reequ field@struct>
                        restore sub@struct \\}
                macro ends \\{ endu \\}
                field@struct equ ,subunion,<
                sub@struct equ subunion \}

        macro endl \{
                restruc db,dw,du,dd,dp,dq,dt
                restruc rb,rw,rd,rp,rq,rt
                purge db,dw,du,dd,dp,dq,dt
                purge rb,rw,rd,rp,rq,rt
                purge union,endl
                irpv fields,field@struct \\{
                        restore field@struct
                 \\common
                        restore @struct
                        stack@depth = 0
                        localroot ,fields
                \\}
                end virtual \}
        irp d@,db,dw,du,dd,dp,dq,dt \{
                struc d@ [val] \\{ \\common define field@struct .,d@,<val> \\}
                macro d@ [val] \\{ \\common \\local anonymous
                                            define field@struct anonymous,d@,<val> \\} \}
        irp r@,rb,rw,rd,rp,rq,rt \{
                match d@,def.\#r@ \\{
                        struc r@ count \\\{ \\\local offset,size
                                             define field@struct .,d@,count dup (?) \\\}
                        macro r@ count \\\{ \\\local anonymous
                                             define field@struct anonymous,d@,count dup (?) \\\} \\} \}
        define @struct
        sub@struct equ
        virtual at 0 }


struct RECT
  left   dd ?
  top    dd ?
  right  dd ?
  bottom dd ?
ends

locals
  A RECT
  b rb 256
  union
    c rb 40
    D RECT
  endu
  l dd 4
endl

  mov   eax,[A.top]
  mov   al,[b+30]
  mov   eax,[c]
  mov   eax,[D]
  mov   eax,[D.top]
  mov   eax,[l]    


And I love in that syntax from first sight as alternative to proc definition & local definition.

Advantages: STRUCTEX.INC not limited with architecture or birness, STACK.INC limited only 32 bit environment as target, LOCALS.INC again not limited with architecture or birness.
Post 06 Feb 2023, 23:32
View user's profile Send private message Send e-mail Reply with quote
ProMiNick



Joined: 24 Mar 2012
Posts: 805
Location: Russian Federation, Sochi
ProMiNick 17 Feb 2023, 13:57
Code:
use32
format binary as 'dll'
include 'helper.inc'
include 'struct.inc'
include 'structex.inc'
eax@frame=0
ebx@frame=0
ecx@frame=0
edx@frame=0
esi@frame=0
edi@frame=0
esp@frame=1
ebp@frame=0
track@offset=0
track@frame=0
stack@offset=0
stack@top equ eax*eax@frame+ebx*ebx@frame+ecx*ecx@frame+edx*edx@frame+esi*esi@frame+edi*edi@frame+(esp+%s)*esp@frame+ebp*ebp@frame+track@offset*track@frame+stack@offset

struc align arg { .:align arg } ; this or
;struc align arg { align arg }  ; this - no matter


caller = 0
callee = 0

macro stack@init arg& {
        stackorg
        off_macro stack@init }

macro stack@retaddr {
        match , @struct \{ define field@struct ret@address,dd,? \}
        match no, @struct \{
                ret@address equ stack@top + anchor@stack
                stack@depth = 4 \} }

macro stack@arg arg {
        match , @struct \{
                match name rest,arg: \\{
                        match :type:,rest \\\{ name type \\\}
                        match :,rest \\\{ define field@struct name,dd,? \\\} \\} \}
        match no, @struct \{
                \local @proxy,@depth
                @depth = stack@depth
                match name rest,arg: \\{
                        name equ stack@top+@depth + anchor@stack
                        define @proxy name
                        stack@depth = stack@depth+4
                        \\}
                match name:type,arg \\{
                        match _name,@proxy \\\{
                                _name substruct type
                                match any =, rest, _name\\\#.fields \\\\{ _locals equ _locals , _name\\\#.fields \\\\} \\\}
                        if defined sizeof.\\#type
                                stack@depth = stack@depth+sizeof.\\#type-4
                        end if \\} \} }

macro RTL_C [arg] {
 common
        ;stack@init
        anchor@stack = -%s
        stack@retaddr
 forward
        stack@arg arg
 common
        match no, @struct \{ callee = stack@depth \} }

macro LTR_pascal [arg] {
 common
        ;stack@init
        anchor@stack = -%s
        stack@retaddr
 reverse
        stack@arg arg
 common
        match no, @struct \{ callee = stack@depth \} }

macro stack_cleanup SC:callee {
        if ~SC
                retn
        else
                retn SC
        end if }

macro frame reg:esp {
        local localsize, ct
        lock_macro frame
        macro endf \{
                ct codetype
                localsize = (stack@depth + (ct-1)) and not (ct-1)
                purge endf \} ; again cancel nesting
        macro exitf \{
                if ~reg eq esp
                        if defined localsize & localsize
                                mov     esp, reg
                        end if
                        pop     reg
                else if defined localsize & localsize
                        add     esp, localsize
                end if  \}

        match any =esp rest,:reg esp: \{
                match :,rest \\{
                        macro via_sp instruction& \\\{
                                track@frame=0
                                esp@frame=1
                                reg#@frame=0
                                match i,instruction \\\\{ i \\\\}
                                track@frame=1
                                esp@frame=0
                                reg#@frame=1 \\\} \\}
                match :,any \\{
                        macro via_sp instruction& \\\{
                                match i,instruction \\\\{ i \\\\} \\\} \\} \}


        if defined localsize & localsize
                sub     esp, localsize
        end if
        if ~reg eq esp
                push    reg
                mov     reg, esp
                track@offset=%s
                track@frame=1
                esp@frame=0
                reg#@frame=1
        end if }

macro recurse@varlocal {
        macro varlocal field,type,def \{
                \local ..depth
                match any,field \\{
                        _locals equ _locals , field
                        field equ stack@top+..depth
                        ..depth type def
                        define try field
                        match _field,try \\\{
                                _field substruct type
                                match any =, rest, _field\\\#.fields \\\\{
                                        _locals equ _locals , _field\\\#.fields \\\\} \\\}
                        restore try \\}
                match ,field \\{
                        recurse@localunion
                        localunion def
                        purge localunion \\} \} }

;recurse@varlocal

macro recurse@localunion {
        macro localunion dummy,[field,type,def] \{
         \common \local size
                size = 0
         \forward
                virtual
                        recurse@varlocal
                        varlocal field,type,<def>
                        purge varlocal
                        if $ > size
                                size = $
                        end if
                end virtual
         \common
                rb size \} }

;recurse@localunion


macro localroot dummy,[field,type,def] {
        recurse@varlocal
        varlocal field,type,<def>
        purge varlocal }

macro locals {
        macro union \{
                macro endu \\{
                        purge endu,ends
                        field@struct reequ field@struct>
                        restore sub@struct \\}
                macro ends \\{ endu \\}
                field@struct equ ,subunion,<
                sub@struct equ subunion \}

        macro endl \{
                restruc db,dw,du,dd,dp,dq,dt
                restruc rb,rw,rd,rp,rq,rt
                purge db,dw,du,dd,dp,dq,dt
                purge rb,rw,rd,rp,rq,rt
                purge union,endl,align,uses
                irpv fields,field@struct \\{
                        restore field@struct
                 \\common
                        restore @struct
                        virtual at -%s
                        localroot ,fields
                        stack@depth = $-$$
                        end virtual
                \\}
                end virtual \}
        macro align amount \{ \local anonymous
                define field@struct anonymous,align,amount \}
        macro uses regs \{
                irps reg,regs \\{
                        \\local anonymous
                        define field@struct anonymous,dd,? \\} \}
        irp d@,db,dw,du,dd,dp,dq,dt \{
                struc d@ [val] \\{ \\common define field@struct .,d@,<val> \\}
                macro d@ [val] \\{ \\common \\local anonymous
                                            define field@struct anonymous,d@,<val> \\} \}
        irp r@,rb,rw,rd,rp,rq,rt \{
                match d@,def.\#r@ \\{
                        struc r@ count \\\{ \\\local offset,size
                                             define field@struct .,d@,count dup (?) \\\}
                        macro r@ count \\\{ \\\local anonymous
                                             define field@struct anonymous,d@,count dup (?) \\\} \\} \}
        ;stack@offset=-%s
        define @struct
        sub@struct equ
        virtual at 0 }

macro flush [args] { irp arg, args \{ match all, arg \\{restore all \\} \} }

macro flush_locals {
        unlock_macro    frame
        purge   exitf,via_sp
        flush   _locals
        irpv any, _locals \{restore _locals \} ; otherwise memory leak at preprocessor stage
        callee = 0 }

    
testsequence:
Code:
struct RECT
  left   dd ?
  top    dd ?
  right  dd ?
  bottom dd ?
ends

hello2: RTL_C g2,h2,i2,j2
push edx
hello: LTR_pascal a1:RECT,b1,c1,d1,f1
        ;stack@init
        ;stackorg
        ;stack@retaddr

frame ebp
locals
  A RECT
  b rb 256
  union
    c rb 40
    D RECT
    union
      d rb 40
      E RECT
      union
        e rb 40
        F RECT
      endu
    endu
  endu
  l dd 4
  ;align $200
  q dd ?
  uses ebp ebx esi
endl
endf
sub esp,$10
locals
  N RECT
endl


        mov eax,[a1.top]
        mov eax,[b1]
        mov eax,[c1]
        mov eax,[d1]
        push edx
        mov eax,[a1.top]
        mov eax,[b1]
        mov eax,[c1]
        mov eax,[d1]
via_sp  mov   [A.top],2
via_sp  mov   [b+30],3
via_sp  mov   [e],4
via_sp  mov   dword[F],5
via_sp  mov   [F.top],6
        mov   [q],7
        mov   [N.left],8
        mov   [A.top],2
        mov   [b+30],3
        mov   [e],4
        mov   dword[F],5
        mov   [F.top],6
        mov   [q],7
        mov   [N.left],8
via_sp  mov   [q],7
        push  esi
via_sp  mov   [A.top],2
via_sp  mov   [b+30],3
via_sp  mov   [e],4
via_sp  mov   dword[F],5
via_sp  mov   [F.top],6
        mov   [q],7
        mov   [N.left],8
via_sp  mov   [q],7
via_sp  mov   [N.left],8
exitf
  stack_cleanup callee
  ;stack_cleanup caller
  flush_locals
  ;mov   eax,[A.top]    

output:
Code:
L_00000000:   push edx
L_00000001:   sub esp, 0x124
L_00000007:   push ebp
L_00000008:   mov ebp, esp
L_0000000A:   sub esp, 0x10
L_0000000D:   mov eax, [ebp+0x140]
L_00000013:   mov eax, [ebp+0x138]
L_00000019:   mov eax, [ebp+0x134]
L_0000001F:   mov eax, [ebp+0x130]
L_00000025:   push edx
L_00000026:   mov eax, [ebp+0x140]
L_0000002C:   mov eax, [ebp+0x138]
L_00000032:   mov eax, [ebp+0x134]
L_00000038:   mov eax, [ebp+0x130]
L_0000003E:   mov dword [esp+0x18], 0x2
L_00000046:   mov byte [esp+0x42], 0x3
L_0000004B:   mov byte [esp+0x124], 0x4
L_00000053:   mov dword [esp+0x124], 0x5
L_0000005E:   mov dword [esp+0x128], 0x6
L_00000069:   mov dword [ebp+0x114], 0x7
L_00000073:   mov dword [ebp-0x10], 0x8
L_0000007A:   mov dword [ebp+0x4], 0x2
L_00000081:   mov byte [ebp+0x2e], 0x3
L_00000085:   mov byte [ebp+0x110], 0x4
L_0000008C:   mov dword [ebp+0x110], 0x5
L_00000096:   mov dword [ebp+0x114], 0x6
L_000000A0:   mov dword [ebp+0x114], 0x7
L_000000AA:   mov dword [ebp-0x10], 0x8
L_000000B1:   mov dword [esp+0x128], 0x7
L_000000BC:   push esi
L_000000BD:   mov dword [esp+0x1c], 0x2
L_000000C5:   mov byte [esp+0x46], 0x3
L_000000CA:   mov byte [esp+0x128], 0x4
L_000000D2:   mov dword [esp+0x128], 0x5
L_000000DD:   mov dword [esp+0x12c], 0x6
L_000000E8:   mov dword [ebp+0x114], 0x7
L_000000F2:   mov dword [ebp-0x10], 0x8
L_000000F9:   mov dword [esp+0x12c], 0x7
L_00000104:   mov dword [esp+0x8], 0x8
L_0000010C:   mov esp, ebp
L_0000010E:   pop ebp
L_0000010F:   ret 0x24    


compiled in fasm with additions described in addition "stack tracing
Post 17 Feb 2023, 13:57
View user's profile Send private message Send e-mail Reply with quote
ProMiNick



Joined: 24 Mar 2012
Posts: 805
Location: Russian Federation, Sochi
ProMiNick 17 Feb 2023, 23:24
Code:
; Simple text editor - fasm example program

format PE GUI 4.0
entry start

include 'win32a.inc'

macro lock_macro [name] {
        name equ name
        macro name \{ err \} }
macro off_macro [name] {
        name equ name
        macro name arg& \{ \} }
macro unlock_macro [name] {
        irpv any,name \{
                purge name
                restore name \} }

; revolution, x86
struc codetype {
        virtual at 0
                inc     ax
                . = 1 shl $
        end virtual }

macro recurse@structurise {
        macro structurise target,source,dummy,[field,type,def] \{
                match any,field \\{
                   match any,target\#.fields \\\{ target\#.fields equ target\#.fields , \\\} 
                   target\#.fields equ target\#.fields target\#.\#field                     
                   target\#.\#field equ target + source\#.\#field
                   define try target\#.\#field
                   match _field,try \\\{
                   recurse@substruct
                   _field substruct type
                   match any =, rest, _field\\\#.fields \\\\{ target\#.fields equ target\#.fields , _field\\\#.fields \\\\} ; this line added it collects subfields
                   restruc substruct \\\}
                   \\}
                match ,field \\{
                recurse@structurise
                structurise target,source,def
                purge structurise
        \\} \} } 
recurse@structurise

macro recurse@substruct {
        struc substruct name \{
                match any =, more,fields@\#name \\{
                        match fields,fields@\#name \\\{
                                recurse@structurise
                                structurise .,name,, fields
                                purge structurise             \\\} \\} \} }
recurse@substruct

macro invoke args& {
        local s
        s=%s
        invoke args
        stackorg s}
esp@frame=1
ebp@frame=0
track@offset=0
track@frame=0
stack@top equ (esp+%s)*esp@frame+ebp*ebp@frame+track@offset*track@frame

caller = 0
callee = 0
struc align arg { .:align arg } ; this or

macro stack@init arg& {
        stackorg
        off_macro stack@init }

macro stack@retaddr {
        match , @struct \{ define field@struct ret@address,dd,? \}
        match no, @struct \{
                ret@address equ stack@top + anchor@stack
                stack@depth = 4 \} }

macro stack@arg arg {
        match , @struct \{
                match name rest,arg: \\{
                        match :type:,rest \\\{ name type \\\}
                        match :,rest \\\{ define field@struct name,dd,? \\\} \\} \}
        match no, @struct \{
                \local @proxy,@depth
                @depth = stack@depth
                match name rest,arg: \\{
                        name equ stack@top+@depth + anchor@stack
                        define @proxy name
                        stack@depth = stack@depth+4
                        \\}
                match name:type,arg \\{
                        match _name,@proxy \\\{
                                _name substruct type
                                match any =, rest, _name\\\#.fields \\\\{ _locals equ _locals , _name\\\#.fields \\\\} \\\}
                        if defined sizeof.\\#type
                                stack@depth = stack@depth+sizeof.\\#type-4
                        end if \\} \} }

macro RTL_C [arg] {
 common
        stack@init
        anchor@stack = -%s
        stack@retaddr
 forward
        stack@arg arg
 common
        match no, @struct \{ callee = stack@depth-4 \} }

macro LTR_pascal [arg] {
 common
        stack@init
        anchor@stack = -%s
        stack@retaddr
 reverse
        stack@arg arg
 common
        match no, @struct \{ callee = stack@depth-4 \} }

macro stack_cleanup SC:callee {
        if ~SC
                retn
        else
                retn SC
        end if }

macro frame reg:esp {
        local localsize, ct
        lock_macro frame
        macro endf \{
                ct codetype
                localsize = (stack@depth + (ct-1)) and not (ct-1)
                purge endf \} ; again cancel nesting
        macro exitf \{
                if ~reg eq esp
                        if defined localsize & localsize
                                mov     esp, reg
                        end if
                        pop     reg
                else if defined localsize & localsize
                        add     esp, localsize
                end if  \}

        match any =esp rest,:reg esp: \{
                match :,rest \\{
                        macro via_sp instruction& \\\{
                                track@frame=0
                                esp@frame=1
                                reg#@frame=0
                                match i,instruction \\\\{ i \\\\}
                                track@frame=1
                                esp@frame=0
                                reg#@frame=1 \\\} \\}
                match :,any \\{
                        macro via_sp instruction& \\\{
                                match i,instruction \\\\{ i \\\\} \\\} \\} \}


        anchor@stack = -%s
        if defined localsize & localsize
                sub     esp, localsize
        end if
        if ~reg eq esp
                push    reg
                mov     reg, esp
                track@offset=%s
                track@frame=1
                esp@frame=0
                reg#@frame=1
        end if }

macro recurse@varlocal {
        macro varlocal field,type,def \{
                \local ..depth,..stack
                match any,field \\{
                        _locals equ _locals , field
                        field equ stack@top+..depth+..stack
                        ..stack = -%s
                        ..depth type def
                        define try field
                        match _field,try \\\{
                                _field substruct type
                                match any =, rest, _field\\\#.fields \\\\{
                                        _locals equ _locals , _field\\\#.fields \\\\} \\\}
                        restore try \\}
                match ,field \\{
                        recurse@localunion
                        localunion def
                        purge localunion \\} \} }

macro recurse@localunion {
        macro localunion dummy,[field,type,def] \{
         \common \local size
                size = 0
         \forward
                virtual
                        recurse@varlocal
                        varlocal field,type,<def>
                        purge varlocal
                        if $ > size
                                size = $
                        end if
                end virtual
         \common
                rb size \} }

macro localroot dummy,[field,type,def] {
        recurse@varlocal
        varlocal field,type,<def>
        purge varlocal }

macro locals {
        macro union \{
                macro endu \\{
                        purge endu,ends
                        field@struct reequ field@struct>
                        restore sub@struct \\}
                macro ends \\{ endu \\}
                field@struct equ ,subunion,<
                sub@struct equ subunion \}

        macro endl \{
                restruc db,dw,du,dd,dp,dq,dt
                restruc rb,rw,rd,rp,rq,rt
                purge db,dw,du,dd,dp,dq,dt
                purge rb,rw,rd,rp,rq,rt
                purge union,endl,align,uses
                irpv fields,field@struct \\{
                        restore field@struct
                 \\common
                        restore @struct
                        virtual at 0;-%s
                        localroot ,fields
                        stack@depth = $-$$
                        end virtual
                \\}
                end virtual \}
        macro align amount \{ \local anonymous
                define field@struct anonymous,align,amount \}
        macro uses regs \{
                irps reg,regs \\{
                        \\local anonymous
                        define field@struct anonymous,dd,? \\} \}
        irp d@,db,dw,du,dd,dp,dq,dt \{
                struc d@ [val] \\{ \\common define field@struct .,d@,<val> \\}
                macro d@ [val] \\{ \\common \\local anonymous
                                            define field@struct anonymous,d@,<val> \\} \}
        irp r@,rb,rw,rd,rp,rq,rt \{
                match d@,def.\#r@ \\{
                        struc r@ count \\\{ \\\local offset,size
                                             define field@struct .,d@,count dup (?) \\\}
                        macro r@ count \\\{ \\\local anonymous
                                             define field@struct anonymous,d@,count dup (?) \\\} \\} \}
        ;stack@offset=-%s
        define @struct
        sub@struct equ
        virtual at 0 }

macro flush [args] { irp arg, args \{ match all, arg \\{restore all \\} \} }

macro flush_locals {
        unlock_macro    frame
        purge   exitf,via_sp
        flush   _locals
        irpv any, _locals \{restore _locals \} ; otherwise memory leak at preprocessor stage
        callee = 0 }    

via esp frame, we could comment & uncomment pair frame exitf
Code:
IDR_ICON = 17
IDR_MENU = 37

IDM_NEW   = 101
IDM_EXIT  = 102
IDM_ABOUT = 901

section '.text' code readable executable

  start: RTL_C

        invoke  GetModuleHandle,0
        mov     [wc.hInstance],eax

        invoke  LoadIcon,eax,IDR_ICON
        mov     [wc.hIcon],eax
        invoke  LoadCursor,0,IDC_ARROW
        mov     [wc.hCursor],eax
        invoke  RegisterClass,wc
        test    eax,eax
        jz      error

        invoke  LoadMenu,[wc.hInstance],IDR_MENU
        invoke  CreateWindowEx,0,_class,_title,WS_VISIBLE+WS_OVERLAPPEDWINDOW,144,128,256,256,NULL,eax,[wc.hInstance],NULL
        test    eax,eax
        jz      error

  msg_loop:
        invoke  GetMessage,msg,NULL,0,0
        cmp     eax,1
        jb      end_loop
        jne     msg_loop
        invoke  TranslateMessage,msg
        invoke  DispatchMessage,msg
        jmp     msg_loop

  error:
        invoke  MessageBox,NULL,_error,NULL,MB_ICONERROR+MB_OK

  end_loop:
        invoke  ExitProcess,[msg.wParam]
flush_locals

WindowProc: RTL_C hwnd,wmsg,wparam,lparam
        ;frame
        push    ebx esi edi
        mov     eax,[wmsg]
        cmp     eax,WM_CREATE
        je      .wmcreate
        cmp     eax,WM_SIZE
        je      .wmsize
        cmp     eax,WM_SETFOCUS
        je      .wmsetfocus
        cmp     eax,WM_COMMAND
        je      .wmcommand
        cmp     eax,WM_DESTROY
        je      .wmdestroy
  .defwndproc:
        invoke  DefWindowProc,[hwnd],[wmsg],[wparam],[lparam]
        jmp     .finish
  .wmcreate:
        invoke  GetClientRect,[hwnd],client
        invoke  CreateWindowEx,WS_EX_CLIENTEDGE,_edit,0,WS_VISIBLE+WS_CHILD+WS_HSCROLL+WS_VSCROLL+ES_AUTOHSCROLL+ES_AUTOVSCROLL+ES_MULTILINE,[client.left],[client.top],[client.right],[client.bottom],[hwnd],0,[wc.hInstance],NULL
        or      eax,eax
        jz      .failed
        mov     [edithwnd],eax
        invoke  CreateFont,16,0,0,0,0,FALSE,FALSE,FALSE,ANSI_CHARSET,OUT_RASTER_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,FIXED_PITCH+FF_DONTCARE,NULL
        or      eax,eax
        jz      .failed
        mov     [editfont],eax
        invoke  SendMessage,[edithwnd],WM_SETFONT,eax,FALSE
        xor     eax,eax
        jmp     .finish
      .failed:
        or      eax,-1
        jmp     .finish
  .wmsize:
        invoke  GetClientRect,[hwnd],client
        invoke  MoveWindow,[edithwnd],[client.left],[client.top],[client.right],[client.bottom],TRUE
        xor     eax,eax
        jmp     .finish
  .wmsetfocus:
        invoke  SetFocus,[edithwnd]
        xor     eax,eax
        jmp     .finish
  .wmcommand:
        mov     eax,[wparam]
        and     eax,0FFFFh
        cmp     eax,IDM_NEW
        je      .new
        cmp     eax,IDM_ABOUT
        je      .about
        cmp     eax,IDM_EXIT
        je      .wmdestroy
        jmp     .defwndproc
      .new:
        invoke  SendMessage,[edithwnd],WM_SETTEXT,0,0
        jmp     .finish
      .about:
        invoke  MessageBox,[hwnd],_about_text,_about_title,MB_OK
        jmp     .finish
  .wmdestroy:
        invoke  DeleteObject,[editfont]
        invoke  PostQuitMessage,0
        xor     eax,eax
  .finish:
        pop     edi esi ebx
        ;exitf
        stack_cleanup callee
flush_locals

section '.data' data readable writeable

  _title TCHAR 'MiniPad',0
  _about_title TCHAR 'About MiniPad',0
  _about_text TCHAR 'This is Win32 example program created with flat assembler.',0
  _error TCHAR 'Startup failed.',0

  _class TCHAR 'MINIPAD32',0
  _edit TCHAR 'EDIT',0

  wc WNDCLASS 0,WindowProc,0,0,NULL,NULL,NULL,COLOR_BTNFACE+1,NULL,_class

  edithwnd dd ?
  editfont dd ?

  msg MSG
  client RECT

section '.idata' import data readable writeable

  library kernel,'KERNEL32.DLL',\
          user,'USER32.DLL',\
          gdi,'GDI32.DLL'

  import kernel,\
         GetModuleHandle,'GetModuleHandleA',\
         ExitProcess,'ExitProcess'

  import user,\
         RegisterClass,'RegisterClassA',\
         CreateWindowEx,'CreateWindowExA',\
         DefWindowProc,'DefWindowProcA',\
         SetWindowLong,'SetWindowLongA',\
         RedrawWindow,'RedrawWindow',\
         GetMessage,'GetMessageA',\
         TranslateMessage,'TranslateMessage',\
         DispatchMessage,'DispatchMessageA',\
         SendMessage,'SendMessageA',\
         LoadCursor,'LoadCursorA',\
         LoadIcon,'LoadIconA',\
         LoadMenu,'LoadMenuA',\
         GetClientRect,'GetClientRect',\
         MoveWindow,'MoveWindow',\
         SetFocus,'SetFocus',\
         MessageBox,'MessageBoxA',\
         PostQuitMessage,'PostQuitMessage'

  import gdi,\
         CreateFont,'CreateFontA',\
         DeleteObject,'DeleteObject'

section '.rsrc' resource data readable

  ; resource directory

  directory RT_MENU,menus,\
            RT_ICON,icons,\
            RT_GROUP_ICON,group_icons,\
            RT_VERSION,versions

  ; resource subdirectories

  resource menus,\
           IDR_MENU,LANG_ENGLISH+SUBLANG_DEFAULT,main_menu

  resource icons,\
           1,LANG_NEUTRAL,icon_data

  resource group_icons,\
           IDR_ICON,LANG_NEUTRAL,main_icon

  resource versions,\
           1,LANG_NEUTRAL,version

  menu main_menu
       menuitem '&File',0,MFR_POPUP
                menuitem '&New',IDM_NEW
                menuseparator
                menuitem 'E&xit',IDM_EXIT,MFR_END
       menuitem '&Help',0,MFR_POPUP + MFR_END
                menuitem '&About...',IDM_ABOUT,MFR_END

  icon main_icon,icon_data,'minipad.ico'

  versioninfo version,VOS__WINDOWS32,VFT_APP,VFT2_UNKNOWN,LANG_ENGLISH+SUBLANG_DEFAULT,0,\
              'FileDescription','MiniPad - example program',\
              'LegalCopyright','No rights reserved.',\
              'FileVersion','1.0',\
              'ProductVersion','1.0',\
              'OriginalFilename','MINIPAD.EXE'    

same but via ebp frame
Code:
WindowProc: RTL_C hwnd,wmsg,wparam,lparam
        frame   ebp
        push    ebx esi edi
        mov     eax,[wmsg]
        cmp     eax,WM_CREATE
        je      .wmcreate
        cmp     eax,WM_SIZE
        je      .wmsize
        cmp     eax,WM_SETFOCUS
        je      .wmsetfocus
        cmp     eax,WM_COMMAND
        je      .wmcommand
        cmp     eax,WM_DESTROY
        je      .wmdestroy
  .defwndproc:
        invoke  DefWindowProc,[hwnd],[wmsg],[wparam],[lparam]
        jmp     .finish
  .wmcreate:
        invoke  GetClientRect,[hwnd],client
        invoke  CreateWindowEx,WS_EX_CLIENTEDGE,_edit,0,WS_VISIBLE+WS_CHILD+WS_HSCROLL+WS_VSCROLL+ES_AUTOHSCROLL+ES_AUTOVSCROLL+ES_MULTILINE,[client.left],[client.top],[client.right],[client.bottom],[hwnd],0,[wc.hInstance],NULL
        or      eax,eax
        jz      .failed
        mov     [edithwnd],eax
        invoke  CreateFont,16,0,0,0,0,FALSE,FALSE,FALSE,ANSI_CHARSET,OUT_RASTER_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,FIXED_PITCH+FF_DONTCARE,NULL
        or      eax,eax
        jz      .failed
        mov     [editfont],eax
        invoke  SendMessage,[edithwnd],WM_SETFONT,eax,FALSE
        xor     eax,eax
        jmp     .finish
      .failed:
        or      eax,-1
        jmp     .finish
  .wmsize:
        invoke  GetClientRect,[hwnd],client
        invoke  MoveWindow,[edithwnd],[client.left],[client.top],[client.right],[client.bottom],TRUE
        xor     eax,eax
        jmp     .finish
  .wmsetfocus:
        invoke  SetFocus,[edithwnd]
        xor     eax,eax
        jmp     .finish
  .wmcommand:
        mov     eax,[wparam]
        and     eax,0FFFFh
        cmp     eax,IDM_NEW
        je      .new
        cmp     eax,IDM_ABOUT
        je      .about
        cmp     eax,IDM_EXIT
        je      .wmdestroy
        jmp     .defwndproc
      .new:
        invoke  SendMessage,[edithwnd],WM_SETTEXT,0,0
        jmp     .finish
      .about:
        invoke  MessageBox,[hwnd],_about_text,_about_title,MB_OK
        jmp     .finish
  .wmdestroy:
        invoke  DeleteObject,[editfont]
        invoke  PostQuitMessage,0
        xor     eax,eax
  .finish:
        pop     edi esi ebx
        exitf
        stack_cleanup callee
flush_locals    

and even everything via ebp frame accessing something via esp is still allowed:
Code:
WindowProc: RTL_C hwnd,wmsg,wparam,lparam
        frame   ebp
        push    ebx esi edi
        mov     eax,[wmsg]
        cmp     eax,WM_CREATE
        je      .wmcreate
        cmp     eax,WM_SIZE
        je      .wmsize
        cmp     eax,WM_SETFOCUS
        je      .wmsetfocus
        cmp     eax,WM_COMMAND
        je      .wmcommand
        cmp     eax,WM_DESTROY
        je      .wmdestroy
  .defwndproc:
via_sp  invoke  DefWindowProc,[hwnd],[wmsg],[wparam],[lparam]
        jmp     .finish
  .wmcreate:
via_sp  invoke  GetClientRect,[hwnd],client
via_sp  invoke  CreateWindowEx,WS_EX_CLIENTEDGE,_edit,0,WS_VISIBLE+WS_CHILD+WS_HSCROLL+WS_VSCROLL+ES_AUTOHSCROLL+ES_AUTOVSCROLL+ES_MULTILINE,[client.left],[client.top],[client.right],[client.bottom],[hwnd],0,[wc.hInstance],NULL
        or      eax,eax
        jz      .failed
        mov     [edithwnd],eax
        invoke  CreateFont,16,0,0,0,0,FALSE,FALSE,FALSE,ANSI_CHARSET,OUT_RASTER_PRECIS,CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,FIXED_PITCH+FF_DONTCARE,NULL
        or      eax,eax
        jz      .failed
        mov     [editfont],eax
        invoke  SendMessage,[edithwnd],WM_SETFONT,eax,FALSE
        xor     eax,eax
        jmp     .finish
      .failed:
        or      eax,-1
        jmp     .finish
  .wmsize:
via_sp  invoke  GetClientRect,[hwnd],client
        invoke  MoveWindow,[edithwnd],[client.left],[client.top],[client.right],[client.bottom],TRUE
        xor     eax,eax
        jmp     .finish
  .wmsetfocus:
        invoke  SetFocus,[edithwnd]
        xor     eax,eax
        jmp     .finish
  .wmcommand:
via_sp  mov     eax,[wparam]
        and     eax,0FFFFh
        cmp     eax,IDM_NEW
        je      .new
        cmp     eax,IDM_ABOUT
        je      .about
        cmp     eax,IDM_EXIT
        je      .wmdestroy
        jmp     .defwndproc
      .new:
        invoke  SendMessage,[edithwnd],WM_SETTEXT,0,0
        jmp     .finish
      .about:
via_sp  invoke  MessageBox,[hwnd],_about_text,_about_title,MB_OK
        jmp     .finish
  .wmdestroy:
        invoke  DeleteObject,[editfont]
        invoke  PostQuitMessage,0
        xor     eax,eax
  .finish:
        pop     edi esi ebx
        exitf
        stack_cleanup callee
flush_locals    

of cource everything is compilable via fasm with additions described in addition "stack tracing" & result exe is valid.
I shrink stack@top complexity to esp & ebp only as usable ones, but macro success operate frame eax, frame ebx and so on if stack@top would have all 8 regs.
All my life I never used fasm feature of skipping unused procedures - I never spend time to code procedures which I not going to use, so wrapping procedure head & body into if used block should goes directly if needed.
Code:
if used WindowProc
WindowProc: RTL_C hwnd,wmsg,wparam,lparam
...
end if    

in my concept procedures is not more exists there are only code thunks with localy visibly names ended with flush_locals, that blocks could be started from RTL_C, from LTR_pascal or from locals macros, and in one code thunk can be multuple count of code thunk entries that share single code thunk.
Post 17 Feb 2023, 23:24
View user's profile Send private message Send e-mail Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Copyright © 1999-2025, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.