Alternative assembler recommendations?

Discussion of hardware and software development for Super NES and Super Famicom. See the SNESdev wiki for more information.

Moderator: Moderators

Forum rules
  • For making cartridges of your Super NES games, see Reproduction.
User avatar
HihiDanni
Posts: 186
Joined: Tue Apr 05, 2016 5:25 pm

Alternative assembler recommendations?

Post by HihiDanni »

Currently my SNES framework is using WLA-DX as its assembler. Technically there isn't anything wrong with this assembler that is straight up preventing me from finishing the framework, but it feels messy and I've had to deal with several workarounds to bugs in its functionality:

- Macros are fragile and inflexible ("invalid use of modulo" bug - I have to make separate versions of the same macro for immediate values and absolute addressing as the arguments).
- Potentially useful functions like RAMSECTIONs don't work due to bugs.
- Seriously, what the hell is a "slot"? This concept is not adequately explained.
- I don't trust it to generate code that won't hit a BRK and thus crash the game.

Since this is a framework I'd like others to be able to use, I'd prefer to have a cleaner codebase than I have now. I would like to know if there are any other assemblers that would fit my needs better. I would also prefer to be able to port my project over to the new assembler relatively easily. Alternatively, this topic can serve to better educate me on anything I might be doing wrong with my existing assembler.

Here's what I'm looking for:

- Fully featured 65816 assembler, generates correct code (duh)
- Ability to include source or binary files inline. I'd like to have human-readable definition files for some of my data.
- Ability to define RAM variables in separate files but not have to manage their starting addresses myself across these files.
- More reliable and readable macros. Additionally, syntax that would make it clear that something is a macro and not a subroutine.
- Handles ROM headers correctly. I don't know what the best practices are.
- Supports a FastROM + HiROM configuration.
- Ability to detect certain programmer mistakes at assemble time would be very nice - specifically calling a subroutine that assumes 16-bit registers when they're currently 8-bit.
- Documentation that isn't a single mile-long text file would be nice.
- Ability to see free space remaining in banks (like WLA already does) would be nice.

Suggestions very much appreciated!
SNES NTSC 2/1/3 1CHIP | serial number UN318588627
User avatar
Drew Sebastino
Formerly Espozo
Posts: 3496
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: Alternative assembler recommendations?

Post by Drew Sebastino »

I think ca65 fits most, if not all, of the criteria. Would you mind posting your source code?
User avatar
rainwarrior
Posts: 8734
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Alternative assembler recommendations?

Post by rainwarrior »

Those points vs ca65:
HihiDanni wrote:- Fully featured 65816 assembler, generates correct code (duh)
- Ability to include source or binary files inline. I'd like to have human-readable definition files for some of my data.
- Ability to define RAM variables in separate files but not have to manage their starting addresses myself across these files.
- More reliable and readable macros. Additionally, syntax that would make it clear that something is a macro and not a subroutine.
- Handles ROM headers correctly. I don't know what the best practices are.
- Supports a FastROM + HiROM configuration.
- Ability to detect certain programmer mistakes at assemble time would be very nice - specifically calling a subroutine that assumes 16-bit registers when they're currently 8-bit.
- Documentation that isn't a single mile-long text file would be nice.
- Ability to see free space remaining in banks (like WLA already does) would be nice.
- AFAIK it generates correct code but it's also actively maintained, so bugs can get fixed if you find any
- .include and .incbin
- .res and linker .segment will lay out your RAM variables, .import / .export / .global will let you share them between files
- the macro system is pretty good, I think, though not quite as good as C preprocessor macros
- you can make a header segment easily, though I think you'll need to generate a checksum with another tool
- the linker is very configurable, so you can lay out the ROM in any arrangement fairly easily
- i think you could create assert macros for this?
- its documentation is HTML, with a hyperlinked table of contents: http://cc65.github.io/doc/ca65.html
- the linker can generate "map" files that give you information like that
User avatar
nicklausw
Posts: 376
Joined: Sat Jan 03, 2015 5:58 pm
Location: ...
Contact:

Re: Alternative assembler recommendations?

Post by nicklausw »

I might as well at least mention my assembler asm16. I'd actually recommend you go ca65 since it would be easier to port WLA code to it but this is a good time for me to see what people want that asm16 may lack.
- Fully featured 65816 assembler, generates correct code (duh)
- Ability to include source or binary files inline. I'd like to have human-readable definition files for some of my data.
- Ability to define RAM variables in separate files but not have to manage their starting addresses myself across these files.
- More reliable and readable macros. Additionally, syntax that would make it clear that something is a macro and not a subroutine.
- Handles ROM headers correctly. I don't know what the best practices are.
- Supports a FastROM + HiROM configuration.
- Ability to detect certain programmer mistakes at assemble time would be very nice - specifically calling a subroutine that assumes 16-bit registers when they're currently 8-bit.
- Documentation that isn't a single mile-long text file would be nice.
- Ability to see free space remaining in banks (like WLA already does) would be nice.
- yes.
- .include and .incbin.
- unfortunately you have to use enums due to lack of a linker phase.
- macros are basically set up like in WLA, except you can pass a modulo just fine. You can also "emulate" an assembler directive by adding a period before macro calls if you really want to.
- not sure what this means. Checksum calculation? Not currently. On my mental to do list.
- yep.
- interesting feature. You can make an .if statement that leads to an .error, but subroutines would have to be called inside a macro.
- it's short and sweet.
- unfortunately not currently.
User avatar
HihiDanni
Posts: 186
Joined: Tue Apr 05, 2016 5:25 pm

Re: Alternative assembler recommendations?

Post by HihiDanni »

I'll take a look at both assemblers tomorrow. It's a bit frustrating that the ca65 template assumes a LoROM configuration so I might need to play around with it some.

One more useful feature that I forgot to mention: The ability to generate a symbol file for debugging purposes.
SNES NTSC 2/1/3 1CHIP | serial number UN318588627
User avatar
Hamtaro126
Posts: 818
Joined: Thu Jan 19, 2006 5:08 pm

Re: Alternative assembler recommendations?

Post by Hamtaro126 »

There was some (of mostly my) planning for a standard assembler for things like this,

My planned feature set was similar to yours, a 6502/65c02/Hu6280/65816/SPC700 multi-assembler with clean 3-part build process similar to ASM6, but unlike ASM6, it was to add Thingy "Table File" support for text,

Thingy was first used as a rom hacking tool for script and text editing, Byuu decided to add it in BASS (a.k.a. XKAS 14) for text support, Any other assembler did not implement this (besides ASAR from SMW Central, But that's for SMW patches!)
AKA SmilyMZX/AtariHacker.
Optiroc
Posts: 129
Joined: Thu Feb 07, 2013 1:15 am
Location: Sweden

Re: Alternative assembler recommendations?

Post by Optiroc »

HihiDanni wrote:One more useful feature that I forgot to mention: The ability to generate a symbol file for debugging purposes.
ca65's linker (ld65) can output a pretty nice symbol file with the `-Ln` option. While trying to get various mapping types correct I've also found use for the `-m` and `-vm` options (export map/verbose map file).
HihiDanni wrote:I'll take a look at both assemblers tomorrow. It's a bit frustrating that the ca65 template assumes a LoROM configuration so I might need to play around with it some.
I was sure that some of the templates I've seen around had a Mode 21/HiROM version, but it seems both blargg's and tepples' templates use Mode 20 exclusively.

It's easy enough to edit the linker configuration for HiROM, though. Here's an example. It's really only the ROM locations in the memory map and the header/vector segments you need to change compared to a LoROM config.
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Alternative assembler recommendations?

Post by tepples »

You'd need to make some other slight changes to the code of my lorom-template project to get it to run in HiROM, such as not assuming that MMIO ports associated with the B Bus ($2100-$2183) and memory controller ($4200-$437F) are within the current data bank.


EDIT: clarified which "the code"
User avatar
HihiDanni
Posts: 186
Joined: Tue Apr 05, 2016 5:25 pm

Re: Alternative assembler recommendations?

Post by HihiDanni »

The game already runs at HiROM, unless you're talking about assembler differences here.

Anyway, I am already seeing two ca65 techniques that I could really use that I hadn't even touched on previously: ability to define stack size within the memory map definition, and the ability to export symbols to other files (which I'm 95% sure WLA can do, but as I said earlier, the readme is kind of a mess to look through).

Currently I'm designing my WRAM layout using an ODS spreadsheet for planning and .enums in code to define them in the program. Obviously I think this approach really sucks and is probably the biggest thing I'd like to improve.
SNES NTSC 2/1/3 1CHIP | serial number UN318588627
User avatar
HihiDanni
Posts: 186
Joined: Tue Apr 05, 2016 5:25 pm

Re: Alternative assembler recommendations?

Post by HihiDanni »

I tried modifying the basic LoROM template on the wiki to be HiROM like the linked example, but I get a range error where the vectors are defined. Specifically, it seems to take issue to the reset symbol:

Code: Select all

    .word $ff, $ff, 0, 0, 0, 0, 0, 0
    .word $ff, $ff, 0, 0, 0, 0, reset, 0
I'm guessing reset is being written as a 24-bit address because ROM0 is technically at $c00000 in the configuration? Some assistance getting a proper linker memory configuration would be appreciated. I want to have code run in LoROM banks (so $008000 at reset, then $808000 when FastROM is engaged), but data be in HiROM banks. How would I achieve this in ld65? I tried modifying the configuration somewhat to have an additional region named "LOROM" starting at $8000. If I do this, the linker "succeeds". however it seems like any code that is inserted pushes the header downward (and also expands the file).

Here is my current ld65 config, which is probably wrong in more ways than one:

Code: Select all

# ca65 linker config for 256K HiROM
# Based on configurations by blargg and David Lindecrantz

SYMBOLS {
    __STACKSIZE__: type = weak, value = $100;
}

# Physical areas of memory
# Names need not match, but it makes it easier to remember if they do.
MEMORY {
    ZEROPAGE:   start =      0, size =  $100;
    LORAM:      start = $000100, size = $1f00 - __STACKSIZE__, define = yes;
    STACK:      start = $002000 - __STACKSIZE__, size = __STACKSIZE__, define = yes;
    HIRAM:      start = $7e2000,    size = $e000, define = yes;
    EXRAM:      start = $7f0000,    size = $10000, define = yes;
    
    LOROM:      start = $8000,      size = $8000;
    ROM0:       start = $c00000,    size = $ffb0, fill = yes, fillval = $00;
    ROMHEADER:  start = $c0ffb0,    size = $50, fill = yes, fillval = $00;
    ROM1:       start = $c10000,    size = $10000, fill = yes, fillval = $00;
    ROM2:       start = $c20000,    size = $10000, fill = yes, fillval = $00;
    ROM3:       start = $c30000,    size = $10000, fill = yes, fillval = $00;
}

# Logical areas code/data can be put into.
SEGMENTS {
    ZEROPAGE:   load = ZEROPAGE,    type = zp,  optional = yes;
    BSS:        load = LORAM,       type = bss, optional = yes;
    LORAM:      load = LORAM,       type = bss, optional = yes;
    HIRAM:      load = HIRAM,       type = bss, optional = yes;
    EXRAM:      load = EXRAM,       type = bss, optional = yes;

    CODE:       load = LOROM,       type = ro, optional = yes;
    RODATA:     load = ROM0,        type = ro;
    HEADER:     load = ROMHEADER,   type = ro, start = $c0ffb0;

    ROM1:       load = ROM1,        type = ro, optional = yes;
    ROM2:       load = ROM2,        type = ro, optional = yes;
    ROM3:       load = ROM3,        type = ro, optional = yes;
}
SNES NTSC 2/1/3 1CHIP | serial number UN318588627
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Alternative assembler recommendations?

Post by tepples »

..., reset - $C00000, ...
AWJ
Posts: 433
Joined: Mon Nov 10, 2008 3:09 pm

Re: Alternative assembler recommendations?

Post by AWJ »

HihiDanni wrote:I tried modifying the basic LoROM template on the wiki to be HiROM like the linked example, but I get a range error where the vectors are defined. Specifically, it seems to take issue to the reset symbol:

Code: Select all

    .word $ff, $ff, 0, 0, 0, 0, 0, 0
    .word $ff, $ff, 0, 0, 0, 0, reset, 0
I haven't used ca65 for SNES development, but I think you want to use .addr, not .word.
Here is my current ld65 config, which is probably wrong in more ways than one:

Code: Select all

# ca65 linker config for 256K HiROM
# Based on configurations by blargg and David Lindecrantz

SYMBOLS {
    __STACKSIZE__: type = weak, value = $100;
}

# Physical areas of memory
# Names need not match, but it makes it easier to remember if they do.
MEMORY {
    ZEROPAGE:   start =      0, size =  $100;
    LORAM:      start = $000100, size = $1f00 - __STACKSIZE__, define = yes;
    STACK:      start = $002000 - __STACKSIZE__, size = __STACKSIZE__, define = yes;
    HIRAM:      start = $7e2000,    size = $e000, define = yes;
    EXRAM:      start = $7f0000,    size = $10000, define = yes;
    
    LOROM:      start = $8000,      size = $8000;
    ROM0:       start = $c00000,    size = $ffb0, fill = yes, fillval = $00;
    ROMHEADER:  start = $c0ffb0,    size = $50, fill = yes, fillval = $00;
    ROM1:       start = $c10000,    size = $10000, fill = yes, fillval = $00;
    ROM2:       start = $c20000,    size = $10000, fill = yes, fillval = $00;
    ROM3:       start = $c30000,    size = $10000, fill = yes, fillval = $00;
}

# Logical areas code/data can be put into.
SEGMENTS {
    ZEROPAGE:   load = ZEROPAGE,    type = zp,  optional = yes;
    BSS:        load = LORAM,       type = bss, optional = yes;
    LORAM:      load = LORAM,       type = bss, optional = yes;
    HIRAM:      load = HIRAM,       type = bss, optional = yes;
    EXRAM:      load = EXRAM,       type = bss, optional = yes;

    CODE:       load = LOROM,       type = ro, optional = yes;
    RODATA:     load = ROM0,        type = ro;
    HEADER:     load = ROMHEADER,   type = ro, start = $c0ffb0;

    ROM1:       load = ROM1,        type = ro, optional = yes;
    ROM2:       load = ROM2,        type = ro, optional = yes;
    ROM3:       load = ROM3,        type = ro, optional = yes;
}
First of all, all RAM regions in the MEMORY section need to have file = "" or else they'll get output to the ROM file (and push everything else out of place). Second, regions that you do want in the ROM file need to be listed in the order you want them written to the ROM, not necessarily the order that they appear in the CPU address space. And everything that gets written to the ROM needs fill = yes so that banks stay aligned.

If you want data in the lower half of each 64K bank and code in the upper half (like Super Robot Wars 4) you need something like this:

Code: Select all

    DATA0:      start = $c00000,    size = $8000, fill = yes, fillval = $00;
    CODE0:      start = $808000,    size = $7fb0, fill = yes, fillval = $00;
    ROMHEADER:  start = $80ffb0,    size = $50,   fill = yes, fillval = $00;

    # repeat for however many code+data banks you need:
    DATA1:      start = $c10000,    size = $8000, fill = yes, fillval = $00;
    CODE1:      start = $818000,    size = $8000, fill = yes, fillval = $00;
    DATA2:      start = $c20000,    size = $8000, fill = yes, fillval = $00;
    CODE2:      start = $828000,    size = $8000, fill = yes, fillval = $00;

    # finally, for HiROM I think data-only banks can all go in one big region,
    # since they're contiguous both in ROM and in CPU address space
    # not 100% sure about this though (ld65 might not allow regions larger than 64K)
    BIGDATA:  start = $cX0000,    size = $XXXXX, fill = yes, fillval = $00;
User avatar
HihiDanni
Posts: 186
Joined: Tue Apr 05, 2016 5:25 pm

Re: Alternative assembler recommendations?

Post by HihiDanni »

AWJ wrote:I haven't used ca65 for SNES development, but I think you want to use .addr, not .word.
It's an interrupt vector, they must specifically be word-sized, unless you know of a way to force it to use just the lower 16 bits. Otherwise I might use tepples' suggestion despite looking kind of hackish.
SNES NTSC 2/1/3 1CHIP | serial number UN318588627
AWJ
Posts: 433
Joined: Mon Nov 10, 2008 3:09 pm

Re: Alternative assembler recommendations?

Post by AWJ »

HihiDanni wrote:
AWJ wrote:I haven't used ca65 for SNES development, but I think you want to use .addr, not .word.
It's an interrupt vector, they must specifically be word-sized, unless you know of a way to force it to use just the lower 16 bits. Otherwise I might use tepples' suggestion despite looking kind of hackish.
.addr does force the output to be word-sized. For a 24-bit pointer you use .faraddr.
Optiroc
Posts: 129
Joined: Thu Feb 07, 2013 1:15 am
Location: Sweden

Re: Alternative assembler recommendations?

Post by Optiroc »

I use the ".loword" directive to get the correct bytes into the vector tables, like so.

According to the manual ".addr" is an alias for ".word", so if true using it should yield the same overflow error.
Post Reply