How do I switch from NROM to UNROM?

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

Post Reply
User avatar
SecretServiceDude
Posts: 99
Joined: Wed Oct 22, 2008 3:49 pm
Location: Los Angeles, CA

How do I switch from NROM to UNROM?

Post by SecretServiceDude »

I've tried to set the mapper number to 2 in the iNES header, and I've toyed around with the ld65 linker config file, but I don't really know what I'm doing. Nestopia gives me a "Corrupt file!" message when I try to load the ROM.

Has anyone got an example handy that could help get me up and running?

If it helps at all, here are the current header and linker config I'm using for NROM:

Code: Select all

.segment "HEADER"
	.byte "NES", 26, 2, 1       ; 32K PRG, 8K CHR

Code: Select all

MEMORY {
	ZP:     start =   $10, size =   $F0, type = rw;
	HEADER: start =     0, size =    16, type = ro, fill = yes;
	ROMX:   start = $8000, size = $6000, type = ro, fill = yes;
	ROM0:   start = $E000, size = $1FF4, type = ro, fill = yes;
	ROMV:   start = $FFF4, size =    $C, type = ro, fill = yes;
	ROM2:   start =     0, size = $2000, type = ro, fill = yes;
	SRAM:   start = $0200, size = $0600, type = rw;
}

SEGMENTS {
	HEADER:   load = HEADER, type = ro;
	CODE:     load = ROM0,   type = ro, align = $100;
	CODE2:    load = ROM0,   type = ro, align = $100, optional=yes;
	RODATA:   load = ROM0,   type = ro;
	STRINGS:  load = ROM0,   type = ro, optional=yes;
	VECTORS:  load = ROMV,   type = ro;
	CHARS:    load = ROM2,   type = ro;
	BSS:      load = SRAM,   type = bss, align = $100;
	ZEROPAGE: load = ZP,     type = zp;
}
Also: Since UNROM uses CHR-RAM, where do I store the CHR data? Does it go into the RODATA segment? And is it necessary to copy 8 KB of CHR data to CHR-RAM before I can render anything?
User avatar
Bregalad
Posts: 8036
Joined: Fri Nov 12, 2004 2:49 pm
Location: Caen, France

Post by Bregalad »

More or less. CHRRAM is that, just a RAM chip. When you power the console, it contains garbage (likely FFs). Usually, you'd want to store your CHR data in PRG ROM, and have a routine that copies it to RAM via $2006/$2007 updates. You don't have to updload it by 8kb chunks, but you can update any amount of tiles you want in any order, that's the big (and sole) advantage of CHRRAM, you're free to re-arrange tiles like you want, and even to change them by software.

I'm no CA64 expert, but you should probably remove the "ROM2" line in order to get it to work.

You should NOT set any CHRROM when doing this, let the byte in iNES header to $00. Only PRGROM data should be present.
Useless, lumbering half-wits don't scare us.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

I believe that Nestopia requires UNROM games to have 128KB of ROM, and it's probably complaining about your 32KB.

Of course you don't have to use the whole 128KB if you don't want to, you could just use the last 32KB. But be sure to place your vectors and reset code in the last 16KB bank, because that's the one UNROM keeps mapped at $C000-$FFFF at all times. Before using the lower half ($8000-$BFFF), be sure to map the correct bank in there.

I'm sorry if I can't help you with assembler settings (my assembler of choice doesn't need any settings).

As Bregalad said, you must define 0 CHR-ROM pages in the header, and where you'll store the data is up to you. Since you're moving from NROM, what would make the most sense would be using one of the 6 new 16KB PRG-ROM banks you got for graphics, and have a simple routine to copy 8KB from it during start up, so it will pretty much feel like NROM for a while, until you decide to take more advantage of the extra ROM space and CHR-RAM freedom.
User avatar
Dwedit
Posts: 4470
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Post by Dwedit »

I've seen 64k sized games which work as Mapper #2 (Bee 52). I'd think it was just complaining that the size in the header does not match the filesize.
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

Dwedit wrote:I've seen 64k sized games which work as Mapper #2 (Bee 52).
I believe this is the case with Zanac as well. The ROM I've checked contains all zeroes in the first 64KB.
I'd think it was just complaining that the size in the header does not match the filesize.
Nestopia seems to only accept mapper 2 games that are 128KB or 256KB, everything else is "corrupted".
User avatar
SecretServiceDude
Posts: 99
Joined: Wed Oct 22, 2008 3:49 pm
Location: Los Angeles, CA

Post by SecretServiceDude »

Okay, I've updated the header and linker config file. Nestopia no longer displays the "Corrupt file!" message; however, now all I've got is a blank screen.

My CHR data is located in the BANK2 segment. I suspect one of my problems is that I'm not successfully copying the data to CHR-RAM. This is my first attempt at bank switching, so I don't really know how to do it. If improper bank switching is the culprit, how do I set it up properly?

Below are my current header and linker config for UNROM:

Code: Select all

.segment "HEADER"
	.byte "NES", $1A            ; "NES" followed by MS-DOS end-of-file
	.byte $08                   ; Size of PRG ROM in 16 KB units (128 KB for UNROM)
	.byte $00                   ; Size of CHR ROM in 8 KB units (Value 0 means the board uses CHR RAM)
	.byte $20, $00              ; Mapper 2 = UNROM/UOROM
	.byte $00                   ; Size of PRG RAM in 8 KB units (Value 0 infers 8 KB for compatibility)

Code: Select all

MEMORY {
	ZP:     start =   $00, size =  $100, type = rw;
	HEADER: start =   $00, size =   $10, type = ro, fill = yes;
	RAM:    start =  $200, size =  $600, type = rw;
	PRG:    start = $8000, size = $8000, type = ro, file = %O;
	PRG2:   start = $C000, size = $4000, type = ro, file = %O;
	PRG3:   start = $C000, size = $4000, type = ro, file = %O;
	PRG4:   start = $C000, size = $4000, type = ro, file = %O;
	PRG5:   start = $C000, size = $4000, type = ro, file = %O;
	PRG6:   start = $C000, size = $4000, type = ro, file = %O;
	PRG7:   start = $C000, size = $4000, type = ro, file = %O;
	PRG8:   start = $C000, size = $4000, type = ro, file = %O;
}
SEGMENTS {
	HEADER:     load = HEADER, type = ro;
	CODE:       load = PRG,  type = ro, start = $8000;
	RODATA:     load = PRG,  type = ro;
	DATA:       load = RAM,  type = bss;
	ZEROPAGE:   load = ZP,   type = zp;
	BSS:        load = RAM,  type = bss, define = yes, align = $100;
	VECTORS:    load = PRG,  type = ro, start = $FFF4;
	BANK2:      load = PRG2, type = ro, start = $C000;
	VECTORS2:   load = PRG2, type = ro, start = $FFF4;
	BANK3:      load = PRG3, type = ro, start = $C000;
	VECTORS3:   load = PRG3, type = ro, start = $FFF4;
	BANK4:      load = PRG4, type = ro, start = $C000;
	VECTORS4:   load = PRG4, type = ro, start = $FFF4;
	BANK5:      load = PRG5, type = ro, start = $C000;
	VECTORS5:   load = PRG5, type = ro, start = $FFF4;
	BANK6:      load = PRG6, type = ro, start = $C000;
	VECTORS6:   load = PRG6, type = ro, start = $FFF4;
	BANK7:      load = PRG7, type = ro, start = $C000;
	VECTORS7:   load = PRG7, type = ro, start = $FFF4;
	BANK8:      load = PRG8, type = ro, start = $C000;
	VECTORS8:   load = PRG8, type = ro, start = $FFF4;
}
User avatar
SecretServiceDude
Posts: 99
Joined: Wed Oct 22, 2008 3:49 pm
Location: Los Angeles, CA

Post by SecretServiceDude »

tokumaru wrote:Of course you don't have to use the whole 128KB if you don't want to, you could just use the last 32KB. But be sure to place your vectors and reset code in the last 16KB bank, because that's the one UNROM keeps mapped at $C000-$FFFF at all times.
For the life of me, I can't figure out how to do that. Strangely, when I was working with NROM, I had no problem placing the vectors and reset code in the last 16KB bank; but with my UNROM project, the program simply refuses to display anything unless the reset code is located precisely at $8000. If I try to relocate the code to $C000, all I get is a black screen.

This is driving me crazy. What's a man to do?
User avatar
SecretServiceDude
Posts: 99
Joined: Wed Oct 22, 2008 3:49 pm
Location: Los Angeles, CA

Post by SecretServiceDude »

YESSSS!!!

After much struggling and frustration, I finally built a working UNROM project! I can now write to CHR-RAM, which I intend to exploit shamelessly for maximum kicking of ass.

Thank you all for your suggestions; I'd have been completely lost without them.

The sky's the limit. I'm stoked. :)
User avatar
clueless
Posts: 496
Joined: Sun Sep 07, 2008 7:27 am
Location: Seatlle, WA, USA

Post by clueless »

I'm just curious... what was the problem? What did you change to make it work?
User avatar
SecretServiceDude
Posts: 99
Joined: Wed Oct 22, 2008 3:49 pm
Location: Los Angeles, CA

Post by SecretServiceDude »

clueless wrote:I'm just curious... what was the problem? What did you change to make it work?
I wasn't aware that I had to place the fixed bank after the other PRG ROM banks in the NES file. I had previously been writing the fixed bank immediately following the iNES header.

Of course, tokumaru told me exactly what to do from the beginning:
tokumaru wrote:But be sure to place your vectors and reset code in the last 16KB bank, because that's the one UNROM keeps mapped at $C000-$FFFF at all times.
I guess I was just being dense. :?


Anyhow, below are the header, linker config, and batch file I use to build my (now working!) UNROM project:


Header:

Code: Select all

.segment "HEADER"
	; Header for UNROM board
	.byte "NES", $1A            ; "NES" followed by MS-DOS end-of-file
	.byte $08                   ; Size of PRG ROM in 16 KB units (128 KB for UNROM)
	.byte $00                   ; Size of CHR ROM in 8 KB units (Value 0 means the board uses CHR RAM)
	.byte $20, $00              ; Mapper 2 = UNROM/UOROM
	.byte $00                   ; Size of PRG RAM in 8 KB units (Value 0 infers 8 KB for compatibility)
Linker config:

Code: Select all

MEMORY {
	M_ZEROPAGE:     start =   $00, size =  $100, type = rw;
	M_RAM:          start =  $200, size =  $600, type = rw;
	M_HEADER:       start =    $0, size =   $10, type = ro, fill = yes, file = "build\ld65\header.bin";
	M_WORKRAM:      start = $6000, size = $2000, type = rw;

	# PRG ROM banks (16 KB each, bank 7 is fixed)
	M_PRGBANK0:     start = $8000, size = $4000, type = ro, fill = yes, file = "build\ld65\bank0.prg";
	M_PRGBANK1:     start = $8000, size = $4000, type = ro, fill = yes, file = "build\ld65\bank1.prg";
	M_PRGBANK2:     start = $8000, size = $4000, type = ro, fill = yes, file = "build\ld65\bank2.prg";
	M_PRGBANK3:     start = $8000, size = $4000, type = ro, fill = yes, file = "build\ld65\bank3.prg";
	M_PRGBANK4:     start = $8000, size = $4000, type = ro, fill = yes, file = "build\ld65\bank4.prg";
	M_PRGBANK5:     start = $8000, size = $4000, type = ro, fill = yes, file = "build\ld65\bank5.prg";
	M_PRGBANK6:     start = $8000, size = $4000, type = ro, fill = yes, file = "build\ld65\bank6.prg";
	M_PRGFIXED:     start = $C000, size = $4000, type = ro, fill = yes, file = "build\ld65\fixed.prg";
}

SEGMENTS {
	HEADER:         load = M_HEADER,   type = ro;
	CODE:           load = M_PRGFIXED, type = ro, start = $C000;
	RODATA:         load = M_PRGFIXED, type = ro;
	VECTORS:        load = M_PRGFIXED, type = ro, start = $FFF0;
	ZEROPAGE:       load = M_ZEROPAGE, type = zp;
	BSS:            load = M_RAM,      type = bss, align = $100;
	PRGBANK0:       load = M_PRGBANK0, type = ro, start = $8000;
	PRGBANK1:       load = M_PRGBANK1, type = ro, start = $8000;
	PRGBANK2:       load = M_PRGBANK2, type = ro, start = $8000;
	PRGBANK3:       load = M_PRGBANK3, type = ro, start = $8000;
	PRGBANK4:       load = M_PRGBANK4, type = ro, start = $8000;
	PRGBANK5:       load = M_PRGBANK5, type = ro, start = $8000;
	PRGBANK6:       load = M_PRGBANK6, type = ro, start = $8000;
}
Batch file:

Code: Select all

@ECHO OFF
SET NESFILE="Bionic Commander (U).nes"
SET BUILD1=build\ca65
SET BUILD2=build\ld65

REM Assemble
call ca65 -I source source\main.s -o %BUILD1%\main.o
call ca65 -I source source\input.s -o %BUILD1%\input.o
call ca65 -I source source\level1.s -o %BUILD1%\level1.o
call ca65 -I source source\util.s -o %BUILD1%\util.o

REM Link
call ld65 --obj-path %BUILD1% -C unrom.cfg main.o input.o level1.o util.o -o ""

REM Create NES file
call copy /b /y %BUILD2%\header.bin %NESFILE%
call copy /b /y %NESFILE% + %BUILD2%\bank0.prg %NESFILE%
call copy /b /y %NESFILE% + %BUILD2%\bank1.prg %NESFILE%
call copy /b /y %NESFILE% + %BUILD2%\bank2.prg %NESFILE%
call copy /b /y %NESFILE% + %BUILD2%\bank3.prg %NESFILE%
call copy /b /y %NESFILE% + %BUILD2%\bank4.prg %NESFILE%
call copy /b /y %NESFILE% + %BUILD2%\bank5.prg %NESFILE%
call copy /b /y %NESFILE% + %BUILD2%\bank6.prg %NESFILE%
call copy /b /y %NESFILE% + %BUILD2%\fixed.prg %NESFILE%
pause
User avatar
clueless
Posts: 496
Joined: Sun Sep 07, 2008 7:27 am
Location: Seatlle, WA, USA

Post by clueless »

It is great that you've got it to work. Please don't take the following as any sort of detraction of your skill.

You don't have to use a batch file. The ld65 linker is fully capable of creating a properly laid out .nes file in iNes format.

At the moment my project uses MMC1 (16K banks) with 4 banks. Three (ROM0, ROM1, TILES) are at $8000 and the non-swappable (ROMK and ROMV) at $c000.

It appears to me that the order the segments are listed in the "SEGMENTS" block does not matter. What appears to matter is the order of the sections "MEMORY" that have the attribute "file = %O". They are written to the output file in that order.

[1] https://www.ecoligames.com/trac/nes-gam ... linker.cfg
User avatar
SecretServiceDude
Posts: 99
Joined: Wed Oct 22, 2008 3:49 pm
Location: Los Angeles, CA

Post by SecretServiceDude »

Everything you said is absolutely correct. In fact, until recently I was using the "file = %O" attribute to have the linker generate the NES file directly. It's true that the order of the segments doesn't matter, only the memory sections.

But still, as a matter of personal preference, I like having the banks separated into distinct files. That way, I can easily throw a bank into a hex editor and immediately see its contents. Also, I imagine that having separate files might make it easier to transfer the game to an actual cartridge later on.

I'm still pretty new at this stuff, so the project will undoubtedly evolve for a while before everything coalesces. At this point, I'm just happy to have something that works, because wrestling with that linker config was a real pain. :P

EDIT: If anyone else out there is struggling to understand how ld65 config files work, this site is pretty useful.
User avatar
clueless
Posts: 496
Joined: Sun Sep 07, 2008 7:27 am
Location: Seatlle, WA, USA

Post by clueless »

(going off topic)

:) Linkers are indeed a dark art. Second only to the dreaded "scsi chain".

http://74.125.45.104/search?q=cache:plY ... =firefox-a

Search for "John Woods".

Than read the quote immediately above that one and immediately below it.
User avatar
clueless
Posts: 496
Joined: Sun Sep 07, 2008 7:27 am
Location: Seatlle, WA, USA

Post by clueless »

SecretServiceDude,

Interesting idea about keeping ROM segments separate to make them easier to "explore" using a hex-editor. You inspired me to hack my build scripts. :)

I now have two linker files and two build targets: The full NES game and just the tiles as if it were a CHRROM game with 16K of tiles.
User avatar
SecretServiceDude
Posts: 99
Joined: Wed Oct 22, 2008 3:49 pm
Location: Los Angeles, CA

Post by SecretServiceDude »

Glad to be of service. After all, it's in my name!

I really love this place because of all the great ideas everyone presents. If these forums didn't exist, I may have given up on my project already, because there are so many things I never could have figured out without all the helpful information/suggestions found here.
Post Reply