CC65 linker config for MMC1 made up of lots of NROMs?

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

Post Reply
tschak909
Posts: 142
Joined: Mon Jul 03, 2017 4:37 pm
Contact:

CC65 linker config for MMC1 made up of lots of NROMs?

Post by tschak909 »

Does anyone have a CC65 linker config that could be used to have a custom menu with a bunch of NROMs to load? I have a bunch of development builds of different ROMs I need to be able to quickly switch between.

I see in NesDEV that MMC1 can be coaxed to work in this manner, but have not made a bank switched ROM with CC65, yet.

-Thom
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: CC65 linker config for MMC1 made up of lots of NROMs?

Post by koitsu »

Start with https://github.com/pinobatch/snrom-template and work from there. Note that this is using ca65/ld65 (read: assembler and linker), not cc65/ld65 (read: compiler and linker).
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: CC65 linker config for MMC1 made up of lots of NROMs?

Post by tepples »

tschak909 wrote:Does anyone have a CC65 linker config that could be used to have a custom menu with a bunch of NROMs to load?
Are these NROM-128 (16 KiB PRG + 8 KiB CHR), NROM-256 (32 KiB PRG + 8 KiB CHR), NROM modded to use CHR RAM (e.g. Tank Demo; RHDE), or a mixture of the three? And does your MMC1 cart use CHR ROM (SLROM) or CHR RAM (SGROM)?
tschak909
Posts: 142
Joined: Mon Jul 03, 2017 4:37 pm
Contact:

Re: CC65 linker config for MMC1 made up of lots of NROMs?

Post by tschak909 »

Standard NROM-128 with 8K CHR-ROM on each.

-Thom
User avatar
rainwarrior
Posts: 8732
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: CC65 linker config for MMC1 made up of lots of NROMs?

Post by rainwarrior »

I think the main problem is just finding ~16 bytes of empty space in each NROM for a reset stub.

Once patched you can just .incbin one of those ROMs into each bank, so most of the linker configuration is pretty flexible. You could really just build your menu as a 16k binary and then concatenate all the other ROM banks with a separate tool (e.g. copy /b a.prg + b.prg ab.prg), for which purpose an NROM-128 linker template is probably almost the same as what you'd need. (A generic MMC1 template has a lot more features than you're actually using for this.)
tschak909
Posts: 142
Joined: Mon Jul 03, 2017 4:37 pm
Contact:

Re: CC65 linker config for MMC1 made up of lots of NROMs?

Post by tschak909 »

Turns out, I really do need the whole 32KB PRG space to be banked out...

This is what I am using so far, and it produces a legal cart that boots up in Nestopia (fceux doesn't like it yet):

Code: Select all

MEMORY {

    ZP:                 start = $0000, size = $0100, type = rw, define = yes;
    HEADER:             start = $0000, size = $0010, file = %O ,fill = yes;
    PRG:                start = $8000, size = $7fc0, file = %O ,fill = yes, define = yes;
    PRG01:              start = $8000, size = $8000, file = %O ,fill = yes, fillval = $FF;
    PRG02:              start = $8000, size = $8000, file = %O ,fill = yes, fillval = $FF;
    PRG03:              start = $8000, size = $8000, file = %O ,fill = yes, fillval = $FF;
    PRG04:              start = $8000, size = $8000, file = %O ,fill = yes, fillval = $FF;
    PRG05:              start = $8000, size = $8000, file = %O ,fill = yes, fillval = $FF;

        DMC:            start = $ffc0, size = $003a, file = %O, fill = yes, define = yes;
        VECTORS:        start = $fffa, size = $0006, file = %O, fill = yes;
    CHR00:              start = $0000, size = $2000, file = %O, fill = yes;
    CHR01:              start = $0000, size = $2000, file = %O, fill = yes;
    CHR02:              start = $0000, size = $2000, file = %O, fill = yes;
    CHR03:              start = $0000, size = $2000, file = %O, fill = yes;
    CHR04:              start = $0000, size = $2000, file = %O, fill = yes;
    CHR05:              start = $0000, size = $2000, file = %O, fill = yes;
    RAM:                start = $0300, size = $0500, define = yes;

          # Use this definition instead if you going to use extra 8K RAM
          # RAM: start = $6000, size = $2000, define = yes;

}

SEGMENTS {

    HEADER:   load = HEADER,         type = ro;
    STARTUP:  load = PRG,            type = ro,  define = yes;
    LOWCODE:  load = PRG,            type = ro,                optional = yes;
    INIT:     load = PRG,            type = ro,  define = yes, optional = yes;
    CODE:     load = PRG,            type = ro,  define = yes;
    RODATA:   load = PRG,            type = ro,  define = yes;
    # STUB00:   load = PRG, type = ro, start = $BFF0;
    # STUB01:   load = PRG01, type = ro, start = $BFF0;
    # STUB02:   load = PRG02, type = ro, start = $BFF0;
    # STUB03:   load = PRG03, type = ro, start = $BFF0;
    # STUB04:   load = PRG04, type = ro, start = $BFF0;
    # STUB05:   load = PRG05, type = ro, start = $BFF0;
    ONCE:     load = PRG,            type = ro, optional = yes;
    DATA:     load = PRG, run = RAM, type = rw,  define = yes;
    VECTORS:  load = VECTORS,        type = ro;
    SAMPLES:  load = DMC,            type = ro;
    PRG1:    load = PRG01,            type = ro, align=$100;
    PRG2:    load = PRG02,            type = ro, align=$100;
    PRG3:    load = PRG03,            type = ro, align=$100;
    PRG4:    load = PRG04,            type = ro, align=$100;
    PRG5:    load = PRG05,            type = ro, align=$100;
    CHR0:    load = CHR00,            type = ro, align=$100;
    CHR1:    load = CHR01,            type = ro, align=$100;
    CHR2:    load = CHR02,            type = ro, align=$100;
    CHR3:    load = CHR03,            type = ro, align=$100;
    CHR4:    load = CHR04,            type = ro, align=$100;
    CHR5:    load = CHR05,            type = ro, align=$100;
    BSS:      load = RAM,            type = bss, define = yes;
    HEAP:     load = RAM,            type = bss, optional = yes;
    ZEROPAGE: load = ZP,             type = zp;
}

FEATURES {

    CONDES: segment = INIT,
            type = constructor,
            label = __CONSTRUCTOR_TABLE__,
            count = __CONSTRUCTOR_COUNT__;
    CONDES: segment = RODATA,
            type = destructor,
            label = __DESTRUCTOR_TABLE__,
            count = __DESTRUCTOR_COUNT__;
    CONDES: type = interruptor,
            segment = RODATA,
            label = __INTERRUPTOR_TABLE__,
            count = __INTERRUPTOR_COUNT__;

}

SYMBOLS {

    __STACKSIZE__: type = weak, value = $0500;          # 5 pages stack

        NES_MAPPER: type = weak, value = 1;                     # mapper number
        NES_PRG_BANKS: type = weak, value = 12;                 # number of 16K PRG banks, change to 2 for NROM256
        NES_CHR_BANKS: type = weak, value = 6;                  # number of 8K CHR banks
        NES_MIRRORING: type = weak, value = 1;                  # 0 horizontal, 1 vertical, 8 four screen

}
and including the chr and prg banks via an .incbin declaration for each.

and I can switch CHR banks, this is good.
now if I can switch PRG banks, and jump back to the RESET vector, without falling off a cliff.. that'd be perfect..

I tried injecting the stub bits from snrom-template, but am wondering if I need to basically cut off the last 16 bytes of the ROM binary I am injecting, to do this? (my rom prg's are exactly 32768 bytes, and chr rom's are 8192 bytes, and contain reset vectors at the end of the 32k PRG..dunno how I am going to shove that into the end of a 16K bank...sigh)

-Thom
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: CC65 linker config for MMC1 made up of lots of NROMs?

Post by lidnariq »

Most NES mappers are only well-defined with ROMs that are powers of two in size. I'm surprised Nestopia is working correctly with only 6x32 KB PRG; it really should reject that.
tschak909
Posts: 142
Joined: Mon Jul 03, 2017 4:37 pm
Contact:

Re: CC65 linker config for MMC1 made up of lots of NROMs?

Post by tschak909 »

Yeah, I kinda figured, so I have padded it out, and it now sees it as an SJROM:
sjrom.PNG
sjrom.PNG (6.91 KiB) Viewed 10705 times
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: CC65 linker config for MMC1 made up of lots of NROMs?

Post by lidnariq »

Does it work in FCEUX? If not, you probably need to have a boot stub in all the "padding" PRG banks too.

MMC1 looks like it was intended to power up on its own in the "last 16K bank is at the top", but hardware testing has shown that it's not very robust.
tschak909
Posts: 142
Joined: Mon Jul 03, 2017 4:37 pm
Contact:

Re: CC65 linker config for MMC1 made up of lots of NROMs?

Post by tschak909 »

lidnariq wrote:Does it work in FCEUX? If not, you probably need to have a boot stub in all the "padding" PRG banks too.

MMC1 looks like it was intended to power up on its own in the "last 16K bank is at the top", but hardware testing has shown that it's not very robust.
Yeah, I got it to work in FCEUX as well, by re-ordering the segments so that the menu program is the very last segment.

Code: Select all

MEMORY {

    ZP:                 start = $0000, size = $0100, type = rw, define = yes;
    HEADER:             start = $0000, size = $0010, file = %O ,fill = yes;
    PRG:                start = $8000, size = $7fc0, file = %O ,fill = yes, define = yes;
    PRG01:              start = $8000, size = $8000, file = %O ,fill = yes, fillval = $FF;
    PRG02:              start = $8000, size = $8000, file = %O ,fill = yes, fillval = $FF;
    PRG03:              start = $8000, size = $8000, file = %O ,fill = yes, fillval = $FF;
    PRG04:              start = $8000, size = $8000, file = %O ,fill = yes, fillval = $FF;
    PRG05:              start = $8000, size = $8000, file = %O ,fill = yes, fillval = $FF;
    PRG06:              start = $8000, size = $8000, file = %O ,fill = yes, fillval = $FF;
    PRG07:              start = $8000, size = $8000, file = %O ,fill = yes, fillval = $FF;

        DMC:            start = $ffc0, size = $003a, file = %O, fill = yes, define = yes;
        VECTORS:        start = $fffa, size = $0006, file = %O, fill = yes;
    CHR00:              start = $0000, size = $2000, file = %O, fill = yes;
    CHR01:              start = $0000, size = $2000, file = %O, fill = yes;
    CHR02:              start = $0000, size = $2000, file = %O, fill = yes;
    CHR03:              start = $0000, size = $2000, file = %O, fill = yes;
    CHR04:              start = $0000, size = $2000, file = %O, fill = yes;
    CHR05:              start = $0000, size = $2000, file = %O, fill = yes;
    CHR06:              start = $0000, size = $2000, file = %O, fill = yes;
    CHR07:              start = $0000, size = $2000, file = %O, fill = yes;
    RAM:                start = $0300, size = $0500, define = yes;

          # Use this definition instead if you going to use extra 8K RAM
          # RAM: start = $6000, size = $2000, define = yes;

}

SEGMENTS {

    HEADER:   load = HEADER,         type = ro;
    PRG1:    load = PRG01,            type = ro, align=$100;
    PRG2:    load = PRG02,            type = ro, align=$100;
    PRG3:    load = PRG03,            type = ro, align=$100;
    PRG4:    load = PRG04,            type = ro, align=$100;
    PRG5:    load = PRG05,            type = ro, align=$100;
    PRG6:    load = PRG06,            type = ro, align=$100;
    PRG7:    load = PRG07,            type = ro, align=$100;
    STARTUP:  load = PRG,            type = ro,  define = yes;
    INIT:     load = PRG,            type = ro,  define = yes, optional = yes;
    CODE:     load = PRG,            type = ro,  define = yes;
    RODATA:   load = PRG,            type = ro,  define = yes;
    ONCE:     load = PRG,            type = ro, optional = yes;
    DATA:     load = PRG, run = RAM, type = rw,  define = yes;
    VECTORS:  load = VECTORS,        type = ro;
    SAMPLES:  load = DMC,            type = ro;
    CHR0:    load = CHR00,            type = ro, align=$100;
    CHR1:    load = CHR01,            type = ro, align=$100;
    CHR2:    load = CHR02,            type = ro, align=$100;
    CHR3:    load = CHR03,            type = ro, align=$100;
    CHR4:    load = CHR04,            type = ro, align=$100;
    CHR5:    load = CHR05,            type = ro, align=$100;
    CHR6:    load = CHR06,            type = ro, align=$100;
    CHR7:    load = CHR07,            type = ro, align=$100;
    BSS:      load = RAM,            type = bss, define = yes;
    HEAP:     load = RAM,            type = bss, optional = yes;
    ZEROPAGE: load = ZP,             type = zp;
}

FEATURES {

    CONDES: segment = INIT,
            type = constructor,
            label = __CONSTRUCTOR_TABLE__,
            count = __CONSTRUCTOR_COUNT__;
    CONDES: segment = RODATA,
            type = destructor,
            label = __DESTRUCTOR_TABLE__,
            count = __DESTRUCTOR_COUNT__;
    CONDES: type = interruptor,
            segment = RODATA,
            label = __INTERRUPTOR_TABLE__,
            count = __INTERRUPTOR_COUNT__;

}

SYMBOLS {

    __STACKSIZE__: type = weak, value = $0500;          # 5 pages stack

        NES_MAPPER: type = weak, value = 1;                     # mapper number
        NES_PRG_BANKS: type = weak, value = 16;                 # number of 16K PRG banks, change to 2 for NROM256
        NES_CHR_BANKS: type = weak, value = 8;                  # number of 8K CHR banks
        NES_MIRRORING: type = weak, value = 1;                  # 0 horizontal, 1 vertical, 8 four screen

}
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: CC65 linker config for MMC1 made up of lots of NROMs?

Post by koitsu »

MMC1 has nuances/complexities relating to its fixed bank (read: different chip revisions behave differently or erratically). Tepples' snrom-template code for MMC1, specifically how resetstub_entry fits into the picture, does its best to work around these quirks.

This is probably why moving your "startup bits" into the last/final bank fixed your problems -- odds are it was breaking in FCEUX as a result of valid CPU vectors not being present in $FFFA-FFFF. In contrast, Mesen gives you a visual layout depicting what PRG-ROM and CHR-ROM bank is mapped to what portions of CPU and PPU addressing space (see bottom of debugger window).
Post Reply