Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace __LINE__ and __FILE__ with something that handles inclusions and repetitions #1120

Open
im-mi opened this issue Dec 30, 2022 · 5 comments
Labels
enhancement Typically new features; lesser priority than bugs rgbasm This affects RGBASM

Comments

@im-mi
Copy link

im-mi commented Dec 30, 2022

These preprocessor symbols are a lot more useful than #1068 and #1072 would have you believe. Here are a few examples*:

* Tested with RGBDS 0.5.2, so they might not work exactly the same in other versions. Also note that many of the macros here require that a symbol named DEBUG be defined (otherwise they function as no-ops).

Anonymous Sections

With anonymous sections, you can create a section without having to give it a name. It's a lot more convenient than having to maintain unique section names. This is especially true if you like to put each function into its own section so that the linker can rearrange things more effectively.

This macro will create a section with an auto-generated name. The name is generated using __FILE__, among other preprocessor symbols.

;* Defines an anonymous section.
;* Uses the same arguments as the "section" command.
macro makeSection
    def file\@ equs __FILE__ ; Remove quotes
    section "__anonymousSection_{s:file\@}\@", \#
    purge file\@
endm

Example usage:

    makeSection romx[$4567], bank[2]
myFunction::
    ; << code here >>
    ret

This results in the creation a section with a name something like __anonymousSection_engine/entity.z80_u38.
Also note the required indentation before makeSection (it is a macro, after all).

Debug Logging

Emulators such as BGB allow you to log messages to the emulator's internal debug console. This is done by using a special instruction/data sequence. Here are some macros for outputting to BGB's debug console. It makes use of anonymous sections so that the actual string data gets stored elsewhere in a non-fixed bank.

;* Writes a line of text to the debug output.
;* If DEBUG is not defined, then no action is taken.
;*
;* @param strSource A label referring to the null-terminated string that will be written.
macro debug_writeLineAt
    if def(DEBUG)
        ld d, d
        jr .debug_writeLineAt_stringData_end\@
        dw $6464
        dw $0001
        dw (\1)
        dw bank(\1)
        .debug_writeLineAt_stringData_end\@
    endc
endm

;* Writes a line of text to the debug output.
;* If DEBUG is not defined, then no action is taken.
;*
;* @param str A string constant that will be written.
macro debug_writeLine
    if def(DEBUG)
        pushs
            makeSection romx
            __string_\@: db \1, 0
        pops
        debug_writeLineAt __string_\@
    endc
endm

Example usage:

myFunction:
    debug_writeLine "This is a debug message."
    ret

Ouput: This is a debug message.

Error Handling

Here, anonymous sections and debug_writeLine are used together with __FILE__ and __LINE__ to print an error message to the debug console and then hang the program. Another variant of debug_fail exists with a condition code argument, but I feel it's a little too verbose to include here.

macro debug_break
    if def(DEBUG)
        ld b, b
    endc
endm

;* Prints an error and halts the program.
;* @param message The message to print.
;* @param [lineNumber]
;*        The current line number.
;*        This should take the form {__LINE__} (including the curly braces).
macro debug_fail
    if def(DEBUG)
        if _NARG == 1
            ; Remove quotes.
            def message\@ equs \1
            debug_writeLine "Error at {s:__FILE__}: {s:message\@}"
            purge message\@
        elif _NARG == 2
            ; Remove quotes.
            def message\@ equs \1
            def line\@ equ \2
            debug_writeLine "Error at {s:__FILE__} line {u:line\@}: {s:message\@}"
            purge line\@
            purge message\@
        else
            fail "Invalid number of arguments."
        endc
        
        di
        .debug_fail_failureLoop\@
            debug_break
        jr .debug_fail_failureLoop\@
    endc
endm

Example usage:

myFunction:
    debug_fail "Oops! Something went wrong.", {__LINE__}
    ret

Output: Error at "Engine/Map.z80" line 681: Oops! Something went wrong.

Note that the line number preprocessor symbol must be written out at the call site as shown above. Otherwise the line number reported by the error message may not be correct. This is the one caveat of using this method.

... anyway, that's all for now.

@Rangi42
Copy link
Contributor

Rangi42 commented Dec 30, 2022

Doing def file\@ equs __FILE__ / ... / purge file\@ is a clever way to get at the "real" filename in a macro, but it's still surprising and inconvenient compared to how __FILE__ works in C. And __LINE__, as you found, really does have to be passed as a literal {__LINE__} argument every time, which is no better than just writing your own line identifier every time:

image

If you want to use real filenames in a macro, you could instead put redef FILENAME EQUS "foo.asm" at the top of every file whose name you care about, and just use FILENAME as-is inside macros.

@pinobatch
Copy link
Member

In the case of "just writing your own line identifier every time", you'd need some sort of preprocessor to change the literal 840 every time a line is added or deleted above line 840 so that the value remains correct.

@eievui5
Copy link
Contributor

eievui5 commented Dec 30, 2022

Rather than un-deprecating these, let’s add new versions that act the way we want? Specifically a line constant that tracks includes and repetitions

@im-mi
Copy link
Author

im-mi commented Dec 30, 2022

Rather than un-deprecating these, let’s add new versions that act the way we want? Specifically a line constant that tracks includes and repetitions

I have to admit this is probably a better course of action. And it would remove the need for weird workarounds.

@ISSOtm
Copy link
Member

ISSOtm commented Jan 7, 2023

For functions, a simple "unique name" is, by necessity, the function's:

SECTION "MyFunction", ROM0 ; (Aside: using this pattern for ROM0 functions is a good idea, but ROMX is not.)
MyFunction::
    ; snip
    ret

For those debug messages, and for a unique name across the whole program, __FILE__ is ill-advised:

$ bat a.asm b.asm common.inc 
───────┬───────────────────────────────────────────────────
       │ File: a.asm
───────┼───────────────────────────────────────────────────
   1   │ INCLUDE "common.inc"
───────┴───────────────────────────────────────────────────
───────┬───────────────────────────────────────────────────
       │ File: b.asm
───────┼───────────────────────────────────────────────────
   1   │ INCLUDE "common.inc"
───────┴───────────────────────────────────────────────────
───────┬───────────────────────────────────────────────────
       │ File: common.inc
───────┼───────────────────────────────────────────────────
   1   │ MACRO make_section
   2   │     def anon_file\@ equs __FILE__
   3   │     SECTION "Anonymous section {anon_file\@}", \#
   4   │     purge anon_file\@
   5   │ ENDM
   6   │ 
   7   │     make_section ROM0
───────┴───────────────────────────────────────────────────
$ rgbasm a.asm -o a.o && rgbasm b.asm -o b.o && rgblink a.o b.o -m /dev/stdout
warning: a.asm(1) -> common.inc(7) -> common.inc::make_section(2): [-Wobsolete]
    `__FILE__` is deprecated
warning: b.asm(1) -> common.inc(7) -> common.inc::make_section(2): [-Wobsolete]
    `__FILE__` is deprecated
error: Section name "Anonymous section common.inc" is already in use

What would be useful would be the output file name... and even then, it's iffy. (The root source file name may not be enough, for example if I run rgbasm -o v1.o configurable.asm -DVERSION=1 and rgbasm -o v2.o configurable.asm -DVERSION=2, though maybe that's niche enough that we can ignore it.) I'd rather advocate for some kind of unique name passed by the build system, e.g. -DUNIT_NAME=main.

@Rangi42 Rangi42 added the rgbasm This affects RGBASM label Feb 7, 2023
@Rangi42 Rangi42 changed the title Un-deprecate __LINE__ and __FILE__ Replace __LINE__ and __FILE__ with something that handles inclusions and repetitions Oct 31, 2023
@Rangi42 Rangi42 added the enhancement Typically new features; lesser priority than bugs label Nov 6, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Typically new features; lesser priority than bugs rgbasm This affects RGBASM
Projects
None yet
Development

No branches or pull requests

5 participants