Here are some more solutions. First, a test case that reproduces your original problem (in one file that can be assembled as is):
struc ctx_t
.next: resd 1
.prev: resd 1
.a: resd 1
.b: resd 1
.c: resd 1
endstruc
section .bss
first_ctx:
istruc ctx_t
at ctx_t.next, resd 1
at ctx_t.prev, resd 1
at ctx_t.a, resd 1
; Missing b!
at ctx_t.c, resd 1
iend
resb 16
Then, my first solution. We use separate multi-line macros (mmacros) specialised for use in nobits
sections, named bss_istruc
and friends. (The bss_istruc
mmacro itself is just an exact copy of NASM's istruc
, but it is consistent to define it along the other two mmacros here.) This is the only solution that requires changes to your code, not just adding some macros.
%imacro bss_istruc 1.nolist
%push
%define %$strucname %1
%$strucstart:
%endmacro
%imacro bss_at 1-2+.nolist
%defstr %$member %1
%substr %$member1 %$member 1
%ifidn %$member1, '.'
resb (%$strucname%1-%$strucname)-($-%$strucstart)
%else
resb (%1-%$strucname)-($-%$strucstart)
%endif
%2
%endmacro
%imacro bss_iend 0.nolist
resb %{$strucname}_size-($-%$strucstart)
%pop
%endmacro
struc ctx_t
.next: resd 1
.prev: resd 1
.a: resd 1
.b: resd 1
.c: resd 1
endstruc
section .bss
first_ctx:
bss_istruc ctx_t
bss_at ctx_t.next, resd 1
bss_at ctx_t.prev, resd 1
bss_at ctx_t.a, resd 1
; Missing b!
bss_at ctx_t.c, resd 1
bss_iend
resb 16
Next, my second solution. We undefine NASM's original istruc
and friends and replace them by mmacros that check for the .bss
section, by name, and will use resb 1
when it is detected, and otherwise db 0
. Obviously, if you use nobits
sections other than the one named .bss
, you would have to modify the detection to make it work.
It also expects that the mmacros are only used after plain section .bss
or segment .bss
directives. If you want to specify section attributes (eg follows=
, align=
, etc), then you need to subsequently add another section
directive that names only the section name without attributes. (This is good form anyway, as use of the __SECT__
macro will warn if your last section
directive included attributes.)
%unimacro istruc 1.nolist
%unimacro at 1-2+.nolist
%unimacro iend 0.nolist
%imacro istruc 1.nolist
%push
%define %$strucname %1
%$strucstart:
%ifidni __SECT__, [section .bss]
%define %$reservespace resb 1
%elifidni __SECT__, [segment .bss]
%define %$reservespace resb 1
%else
%define %$reservespace db 0
%endif
%endmacro
%imacro at 1-2+.nolist
%defstr %$member %1
%substr %$member1 %$member 1
%ifidn %$member1, '.'
times (%$strucname%1-%$strucname)-($-%$strucstart) %$reservespace
%else
times (%1-%$strucname)-($-%$strucstart) %$reservespace
%endif
%2
%endmacro
%imacro iend 0.nolist
times %{$strucname}_size-($-%$strucstart) %$reservespace
%pop
%endmacro
struc ctx_t
.next: resd 1
.prev: resd 1
.a: resd 1
.b: resd 1
.c: resd 1
endstruc
section .bss
first_ctx:
istruc ctx_t
at ctx_t.next, resd 1
at ctx_t.prev, resd 1
at ctx_t.a, resd 1
; Missing b!
at ctx_t.c, resd 1
iend
resb 16
Next, a solution that does not work. The idea is to replace the use of db 0
in the mmacros by a macro that will expand to a number of resb
directives. This does not work because times
is an assembler directive, not a preprocessor directive, so the db 0
is never evaluated as an mmacro. Reproduced here for posterity.
struc ctx_t
.next: resd 1
.prev: resd 1
.a: resd 1
.b: resd 1
.c: resd 1
endstruc
section .bss
first_ctx:
%imacro resb_for_every 1-*.nolist
%rep %0
%ifstr %1
%strlen %%length %1
resb %%length
%else
resb 1
%endif
%rotate 1
%endrep
%endmacro
%idefine db resb_for_every
istruc ctx_t
at ctx_t.next, resd 1
at ctx_t.prev, resd 1
at ctx_t.a, resd 1
; Missing b!
at ctx_t.c, resd 1
iend
%undef db
resb 16
Next, a revision of the above which makes it work, by using only a single-line macro (smacro) definition for the db
. This is a super hack solution, but it does work if you take care to only ever replace db 0
using this trick. (ETA: Modified slightly to allow any db
with a single number to work.)
struc ctx_t
.next: resd 1
.prev: resd 1
.a: resd 1
.b: resd 1
.c: resd 1
endstruc
section .bss
first_ctx:
%idefine db resb 1 + 0 *
istruc ctx_t
at ctx_t.next, resd 1
at ctx_t.prev, resd 1
at ctx_t.a, resd 1
; Missing b!
at ctx_t.c, resd 1
iend
%undef db
resb 16
Finally, another take on the non-working solution: Instead of the db
directive, we replace the times
directive by a special mmacro. This mmacro will check for a db 0
string at the end of its parameter, making it safer than the prior hack.
struc ctx_t
.next: resd 1
.prev: resd 1
.a: resd 1
.b: resd 1
.c: resd 1
endstruc
section .bss
first_ctx:
%imacro times_resb 1+.nolist
%defstr %%our_string %1
%strlen %%our_string_length %%our_string
%substr %%our_db_0 %%our_string %%our_string_length + 1 - 5,-1
%ifidni %%our_db_0, " db 0"
%substr %%our_number_string %%our_string 1,-6
%deftok %%our_number_token %%our_number_string
resb %%our_number_token
%else
%error Unexpected use of times_resb macro: %%our_string %%our_db_0
%endif
%endmacro
%idefine times times_resb
istruc ctx_t
at ctx_t.next, resd 1
at ctx_t.prev, resd 1
at ctx_t.a, resd 1
; Missing b!
at ctx_t.c, resd 1
iend
%undef times
resb 16