Randomizers often need a LOT of code for managing the meta information for the game, and so this assembler is designed for generating modules that can patch existing modules at link time. For instance, using `.org` will default to overwriting the base code from the game, and `.reloc` generates relocatable blocks. `.free` can be used to mark sections of the game as unused which will let the linker place relocatable blocks in there.
The catch is i'm doing the Assembling and linking in the C# so the rest of the C# code for the randomizer can interact with the ASM module, and I'm creating a custom C# class to represent all of the different expressions in the syntax. So if you wanted to write ASM in C# it would look like
Code: Select all
var a = new Asm(org: 0x8000, segment: Segments[0x2]);
var PowerOfTwos = a.label("PowerOfTwos");
a.db(0x1, 0x2, 0x4, ...); // etc...
var Exit = a.label("Exit");
a.rts();
a.lda("MyValue"); // link time value loads the Label and uses zp or abs depending on the label type
a.tay();
a.lday(PowerOfTwos); // uses Abs addressing since its a label
a.cmp(Imm(0x20));
a.bne(Exit);
a.rts();
So this is where i feel I can do something novel, C# supports a feature called SourceGenerator (and the new IncrementalGenerator) which allows you to register a custom compiler step to generate C# source files. After I get the basic building blocks that i wrote above compiling and linking, I plan to write a custom 6502 parser/lexer that will read regular 6502 asm files and spit out generated blocks like above. In your C# project, you'll be able to include full assembly source files and import/reference the modules from the C# code.
Whats neat about this approach is the C# compiler will be doing type checking for the assembly, and using the `#line` feature, I can actually display the errors from the generated source directly into the ASM file, with error underlining and everything.
Code: Select all
BasicLabel:
lda $80, y ; red squiggly under $80: error on line 2 no such function lda_y that takes ZeroPage
Code: Select all
var a = new Asm();
#line (1,0)-(1-10) "asmfile.s"
var BasicLabel = a.label("BasicLabel");
#line (2,3)-(2-6) "asmfile.s"
a.lda_y(
#line (2-8)-(2-11) "asmfile.s"
ZeroPage(0x80) // This generated code has an error since theres no overload for lda_y that takes a ZeroPage
);
Code: Select all
public partial class Foo {
[Asm("""
.segment "2"
.org $8000 ; Location where the player movement is checked
; Patch a function in the original game to call our custom handler
jsr PreventPlayerFromGoingOffscreen
PreventPlayerFromGoingOffscreen:
lda PlayerX
cmp #$ef
bcc +
; do something
+ ; do original hook code here
rts
""")]
private Module PreventPlayerFromGoingOffscreen;
// Just example code for how it can be used
void Randomizer(Flags flags) {
if (flags.DontGoOffscreen)
Assembler.AddModule(PreventPlayerFromGoingOffscreen);
}
}
So yeah, I think this could be really handy for these kinds of ROM hacking/ randomizer projects where you want a lot of options toggled on/off with high level code to dictate how its to be used