-
-
Notifications
You must be signed in to change notification settings - Fork 171
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
[Feature request] C support #989
Comments
A few notes:
All in all, this is a resounding "yes" from me! This should finally bridge the gap we've had for so long—I'm actually flabbergasted we haven't thought of it earlier. |
This is a mirror of doing it the other way around ( A more complicated case would be that of
I'd say that #916 is complementary to this request, not a blocker. If a way to designate the type of a symbol is added to RGBASM, then, after #916 is implemented, C structures could be used when defining a label to automatically create local members. For example:
This example introduces a hypothetical
I'd replicate the same behavior that currently exists for
I'll address inlining in a separate message, since it's a complex topic. |
Inlining a pure assembly function directly into C code would be problematic, for the reasons stated above. In particular, assembly functions don't have a well-defined boundary; they also lack any formal way of specifying their inputs and outputs, and therefore cannot be safely used when interacting with C code. The solution to this problem is to write assembly code directly within C. Of course, within these assembly blocks, the full syntax of RGBASM assembly would be available (with the exception of C blocks themselves, since nesting blocks that way is confusing and not useful). For example:
(Note that this function is defined outside of any sections, as it is marked Many compilers allow defining a "naked" assembly function (i.e., a function without a prologue or epilogue), but this wouldn't be necessary here, as the function can be defined entirely in assembly and simply forward-declared in C code. (See the |
In my opinion, this is a prime example of feature creep. This functionality would best be provided by an optional macro package, there's no need to include it in RGBDS's core. |
Oh duh, you're totally right. Marking as WONTFIX INVALID APRILFOOLS. |
There's always been an unnecessary conflict between C programmers and assembly programmers in the community, mostly due to the underperformance of the former and the developer complexity of the latter. While there's little doubt that there are tasks in a GB game (or program, in general) that are more suitable for one or the other, the impossibility of mixing both in a friendly manner (particularly when considering toolchain incompatibility) has continuously led projects into making a difficult choice right at the start.
Where using small snippets of assembly code in a primarily C codebase is a need that is already catered to some extent by GBDK, the opposite is an unfulfilled niche.
Therefore, my proposal and request is to allow the direct inclusion of C code in assembly codebases in RGBASM, which the toolchain could compile suitably. Taking advantage that
C
andENDC
are already keywords, such a block could be introduced without much difficulty to the language:When encountering such a block, RGBASM would compile it (using some reasonable optimization configuration), producing the equivalent assembly. In a code section (ROM0/ROMX), the C code must only contain functions and read-only globals (
const
or arrays thereof); in a data section (WRAM0, etc.), read-only and regular globals could be defined, but not functions. Pure declarations (e.g., forwards) should be allowed anywhere, including outside of sections.Since RGBASM already has a concept of a translation unit, such a concept would extend to the C code defined therein for purposes of scoping; likewise, any C block can use names defined in a previous block in the same translation unit. (This enables the use of headers.)
While an implementation of C in RGBASM should be a fully-compliant freestanding C17 implementation (or realistically, by the time the feature is finished, C23), focus should be placed on features that a GB/GBC game could realistically use. For instance, while there should be an implementation of floating point (
float
,double
andlong double
are mandatory types, and their minimum ranges essentially require the use of true floating point to represent them; plus, the odd users using these types would expect IEEE floats), it should not be the focus of optimizations: anyone using floating point in a GBC program (for example, a scientific calculator program) expects it to be slow.A C implementation that interoperates with assembly requires a well-defined calling convention so that both languages can call into each other. This must be documented; however, it makes little sense to make it configurable, as it would inhibit development of the optimizer (which is necessary to achieve any acceptable performance from C code). My suggestion is the following:
hl
,de
,bc
in that order. The first 8-bit argument is passed ina
; other 8-bit arguments use registers from the 16-bit list not yet accounted for. (A function taking(uint8_t, uint8_t, uint16_t)
would have its arguments ina
,d
,hl
in that order.)a
, 16-bit values inhl
, 32-bit values inhlde
. Anything wider than 32 bits (uint64_t
, structs, etc.) takes a pointer to the return buffer inhl
; in this case,hl
is not available for arguments.A conformant C implementation requires a preprocessor, and this case is no different. However, such a preprocessor must only be applied to
C
blocks. Some special cases are of interest:#define
for object-like macros (i.e., those defined without arguments) should use the same namespace asEQUS
, for simplicity. This means thatEQUS
definitions from assembly would be available in C, and#define
macros from C would be available in assembly. (C function-like macros have no equivalent in RGBASM, so those would only be available within C blocks. However, the implementation could require them to not match the name of anEQUS
or an object-like macro. Another alternative would be to introduce function-like macros to assembly, although that might collide with other proposals.)#undef
would therefore behave likePURGE
.#include <file>
should only pull from the standard library, or perhaps a user-defined inclusion directory.#include "file"
, iffile
exists, would behave exactly likeINCLUDE
in assembly. (As per C17, section 6.10.2, clause 3, if#include "file"
fails to find that file, it should behave like#include <file>
.) Including a file from C code would allow straight-up C files to exist in the codebase: as the contents of the included file are virtually transcluded at the point of inclusion, they would be included within aC
block, and thus would be processed as C code.#if
...#endif
blocks should begin and end within the sameC
block. While it's entirely possible to make them interact withIF
...ENDC
blocks,ENDC
itself is being used as an ending marker for theC
block as a whole (intentionally so, as it disallows this harmful interaction), and the contexts in which the corresponding expressions are evaluated is completely different, which makes them non-interchangeable.The concept of a standard library is meaningful in itself. A freestanding C implementation doesn't require any functions to be defined by the standard library. (C17, section 4, clause 6.) Therefore, any functions used by
C
blocks would have to be implemented by the user elsewhere. (RGBASM may provide a standard library of functions in its headers; however, since there is no default location for RGBASM to place code (as RGBASM doesn't normally generate any code not submitted by the user), it would also have to provide the facilities for users to designate the location where that code would be included in the resulting binary. This is probably excessive for an initial implementation.)There are no provisions for banking or an entry point in the previous description. A freestanding C implementation doesn't have a defined entry point; there is no special
main
function. Since the environment already has a well-defined entry point (at address 256), there is no need to introduce a new one.As for banking considerations, since the primary use of the feature is defining locally-relevant blocks of C code, it should be left entirely to the user. It's entirely possible for users to define their own banking functions where needed, like so:
I hope my exposition has been clear, and I'm sure the compelling benefits of this idea will lead to a quick implementation. I'm open for any questions you might have.
The text was updated successfully, but these errors were encountered: