It is currently Tue Jul 23, 2019 2:07 am

All times are UTC - 7 hours





Post new topic Reply to topic  [ 11 posts ] 
Author Message
PostPosted: Wed May 01, 2019 9:07 pm 
Offline

Joined: Mon Jul 03, 2017 4:37 pm
Posts: 134
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


Top
 Profile  
 
PostPosted: Wed May 01, 2019 9:46 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 4156
Location: A world gone mad
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).


Top
 Profile  
 
PostPosted: Thu May 02, 2019 5:37 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 21511
Location: NE Indiana, USA (NTSC)
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)?

_________________
Pin Eight | Twitter | GitHub | Patreon


Top
 Profile  
 
PostPosted: Thu May 02, 2019 1:52 pm 
Offline

Joined: Mon Jul 03, 2017 4:37 pm
Posts: 134
Standard NROM-128 with 8K CHR-ROM on each.

-Thom


Top
 Profile  
 
PostPosted: Thu May 02, 2019 2:07 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 7528
Location: Canada
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.)


Top
 Profile  
 
PostPosted: Sun May 19, 2019 11:48 am 
Offline

Joined: Mon Jul 03, 2017 4:37 pm
Posts: 134
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:
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


Top
 Profile  
 
PostPosted: Sun May 19, 2019 12:11 pm 
Offline

Joined: Sun Apr 13, 2008 11:12 am
Posts: 8488
Location: Seattle
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.


Top
 Profile  
 
PostPosted: Sun May 19, 2019 12:22 pm 
Offline

Joined: Mon Jul 03, 2017 4:37 pm
Posts: 134
Yeah, I kinda figured, so I have padded it out, and it now sees it as an SJROM:

Attachment:
sjrom.PNG
sjrom.PNG [ 6.91 KiB | Viewed 5457 times ]


Top
 Profile  
 
PostPosted: Sun May 19, 2019 12:36 pm 
Offline

Joined: Sun Apr 13, 2008 11:12 am
Posts: 8488
Location: Seattle
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.


Top
 Profile  
 
PostPosted: Sun May 19, 2019 12:57 pm 
Offline

Joined: Mon Jul 03, 2017 4:37 pm
Posts: 134
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:
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

}


Top
 Profile  
 
PostPosted: Sun May 19, 2019 2:45 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 4156
Location: A world gone mad
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).


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 11 posts ] 

All times are UTC - 7 hours


Who is online

Users browsing this forum: Google Adsense [Bot] and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group