Hello World
Moderator: Moderators
Oh, I see; cc65 uses that linker, so they hooked ca65 into it also.It acts like a C compiler because it's bundled with one
I see, though I can't promise I'll turn out any games.Let me tell you part of why I don't bite newbies: I want to help demonstrate the legality of NES emulators. To be legal under US law, a copying technology has to have a substantial noninfringing use. Debian (and hence Ubuntu) accepts NES emulators, but Fedora doesn't because someone on fedora-legal thinks the three dozen or so noninfringing ROMs on pdroms.de are not substantial compared to the hundreds of infringing ROMs in a typical GoodNES set. But every time we train an eager newbie to be an NES coder or artist, we potentially get one step closer to a substantial library of playable homebrew games.
Ok.Yeah, it screws it up, don't use it. Seriously, I tried to use ".org" to create a very specific layout for my zero-page variables, to make viewing them in FCEUX's debugger easier. However, after using ".org" and ".reloc" a few times, the assembler produced unlinkable code. I don't remember the exact error. So I replaced my usage of ".org" with lots of ".aligns" and ".assert" and I hand-tune my variable declaration list whenever an assert fails. I also use lots of "structs", but I'm a C junkie.
Ok, I've enabled them in the .bat file that assembles and links everything.I also recommend that you enable the linker map file and debug file and review them. They will show you where the linker actually put stuff.
Nope, they're all zero.Also just load the ROM into FCEUX and look at it in the debugger. Are the three 6502 vectors ($fffa - $ffff) set properly?
I meant "your" not as in that thread, but as in this thread: http://nesdev.com/bbs/viewtopic.php?p=37306 , which contains a link to that thread.I think that you are slightly mistaken. I did not create that thread, I have no posts in it, and its only one page long.
It's not a bug in ca65. It's that one forgets to prepend the # to the beginning of an immediate value, thus instead using the value at an address, which might sometimes work and sometimes not.I never noticed the bug in ca65.
This NovaYoshi sounds scary. I don't recall reading any posts by him.Certainly. You are most welcome. We feed rude noobs to NovaYoshi. We keep the good ones.
Ok, here's what I have:If you wish, post your linker config somewhere and we'll take a look at it.
Code: Select all
# Linker
#---------------------------------------------------------------------------
MEMORY {
# .nes Header
HEADER: start = $0000, size = $0010, file = %O, define = no;
# fix
}
#---------------------------------------------------------------------------
SEGMENTS {
HEADER: load = HEADER, type = ro, define = no;
# fix
}
#---------------------------------------------------------------------------
-----
A final question: Say I include a file that has some scopes with variables and/or enumerations. I shouldn't need to define it as a segment and link it, right? But what if the file included a procedure? or a macro?
There's nothing else to look at though. I posted the entirety of the linker file.clueless wrote:On first glance, your linker config file looks syntacticly correct.
If you wish, place a zip file (or tar ball if your on unix) someplace where I can download it (pm me if you want to keep the url a secret) and I'll take a really close look at it for you.
Edit: You're response made me uneasy because it was unexpected. So I made a new linker file and literally just copied and pasted the code from the old file into the new one, and it links like it's supposed to. This time I get a """Missing memory area assignment for segment 'CODE'""" error, which rather obviously points out what is wrong.
One of the things that I was going to look for in your raw file was the inclusion of any non-printable characters. The parser for the linker will find them, even if your editor hides them.
What I meant by posting a zip file (I should have been clearer) was a zip file containing your entire project, not just the linker file. I did not realize that was your entire project to date. I assumed that your linker file started out with more segments, you got the error, and then reduced the problem space down to the smallest that it could be and still exhibit the defect, then posted.
cc65, ca65, ld65 have a steeper learning curve than the other common nesdev tool kits. But the cc65 project provides much more power and control over your final binary. I think that you'll do fine if you keep at it.
I stumbled through these frustrations myself. Writing the assembly was ok, as I already knew 6502 assembly and was familiar with various assembler syntaxes. For me, the hard part was getting the linker to arrange my pieces where I wanted them. Then one day it just clicked.
I used to use cc65 to cross compile code for the Apple IIe years ago. Each test cycle I would send my program over a 57600 serial null modem cable using zmodem to a real Apple IIe.
What I meant by posting a zip file (I should have been clearer) was a zip file containing your entire project, not just the linker file. I did not realize that was your entire project to date. I assumed that your linker file started out with more segments, you got the error, and then reduced the problem space down to the smallest that it could be and still exhibit the defect, then posted.
cc65, ca65, ld65 have a steeper learning curve than the other common nesdev tool kits. But the cc65 project provides much more power and control over your final binary. I think that you'll do fine if you keep at it.
I stumbled through these frustrations myself. Writing the assembly was ok, as I already knew 6502 assembly and was familiar with various assembler syntaxes. For me, the hard part was getting the linker to arrange my pieces where I wanted them. Then one day it just clicked.
I used to use cc65 to cross compile code for the Apple IIe years ago. Each test cycle I would send my program over a 57600 serial null modem cable using zmodem to a real Apple IIe.
Maybe that was it; I can't check because I've already deleted/exorcised the old linker file.Posted: Wed Jan 12, 2011 8:38 pm Post subject:
One of the things that I was going to look for in your raw file was the inclusion of any non-printable characters. The parser for the linker will find them, even if your editor hides them.
Ok then, so how do I know where to put the CODE segment? Is this what's called the PRG ROM bank? I'm trying to use MMC3 (http://wiki.nesdev.com/w/index.php/MMC3 ).
You will probably have many code segments. One in each bank, at least.
Also, some people like to separate "read only data" (think internal data tables) from "code". You don't need to do this, but you may choose to if you wish.
I do not have an MMC3 example, but I do have one MMC1 example and one NROM example. The first one is from my first nesdev attempt, back in 2008.
MMC1 provides for 16 16K rom banks (well, it might do more, i don't remember. My game wanted to use 16 of them). The top bank is not switched, and is always at $c000 to $ffff (the MMC1 allows other configs, but this is the one that I choose). The permanent segment is called KERNEL (see very bottom of my linker.cfg).
The other "ROM_0" through "ROM_E" are all bank-switched at $8000 to $bfff. I decided to put the bank number at $bfff in each bank, so that code running in KERNEL can push/pop the active bank during an NMI, should it need to bankswitch to handle the NMI. That is the "ROM_x_MARKER" segment. You can ignore those.
The linker section "SEGMENTS" defines the order that the segments are layed out in the ROM file. That is why "HEADER" is first. "ZEROPAGE", "PLAYER" and "DATA" all define RAM, so those are not stored in the final binary. However, the linker needs to know about them so that it can assign addresses and do the "address fixups" when emitting the actual binary.
Note that my MMC1 example uses CHAR-RAM, so you don't see a CHAR-ROM in the SEGMENTS or MEMORY tables.
https://www.ecoligames.com/trac/nes-gam ... linker.cfg
My second example, NROM:
Below is the linker file for my Yars' Revenge game. This game DOES use char-rom, but no bank-switching. prog-rom is 16k, char-rom is 8k. Note that in this config, I define four memory ranges that all get linked into the same segment: BRAND, RODATA, KERNEL and VECTORS. The linker puts them into the same address space, in the segment called "KERNEL". I just choose to use my own segment names instead of the ld65 defaults. When you see "KERNEL", think "CODE", so my examples might be a bit confusing.
I also split the NES's 2K of ram into different memory regions, and I use the linker config file to assign these memory regions to absolute addresses. QSHIELD is a 128 byte buffer of tiles to be blitted to the name-table, 2 columns 16 bytes tall, every NMI (the entire translated QS is stored in ram). NZONE contains the neutral zone tiles that are to be blitted to the name-table, 2 columns 30 tiles tall, every NMI (but only two columns are stored in RAM). I didn't have to do it this way.
I'm showing this because I think that it help illustrate the flexibility of what the linker can do for you.
btw, the 6502 only has 3 vectors. You'll notice that my linker config allocated 10 bytes instead of 6. I use the extra four bytes to store the assembler time stamp of when the build was made, thusly:
And I include my char-rom like this:
And the header:
Also, some people like to separate "read only data" (think internal data tables) from "code". You don't need to do this, but you may choose to if you wish.
I do not have an MMC3 example, but I do have one MMC1 example and one NROM example. The first one is from my first nesdev attempt, back in 2008.
MMC1 provides for 16 16K rom banks (well, it might do more, i don't remember. My game wanted to use 16 of them). The top bank is not switched, and is always at $c000 to $ffff (the MMC1 allows other configs, but this is the one that I choose). The permanent segment is called KERNEL (see very bottom of my linker.cfg).
The other "ROM_0" through "ROM_E" are all bank-switched at $8000 to $bfff. I decided to put the bank number at $bfff in each bank, so that code running in KERNEL can push/pop the active bank during an NMI, should it need to bankswitch to handle the NMI. That is the "ROM_x_MARKER" segment. You can ignore those.
The linker section "SEGMENTS" defines the order that the segments are layed out in the ROM file. That is why "HEADER" is first. "ZEROPAGE", "PLAYER" and "DATA" all define RAM, so those are not stored in the final binary. However, the linker needs to know about them so that it can assign addresses and do the "address fixups" when emitting the actual binary.
Note that my MMC1 example uses CHAR-RAM, so you don't see a CHAR-ROM in the SEGMENTS or MEMORY tables.
https://www.ecoligames.com/trac/nes-gam ... linker.cfg
My second example, NROM:
Below is the linker file for my Yars' Revenge game. This game DOES use char-rom, but no bank-switching. prog-rom is 16k, char-rom is 8k. Note that in this config, I define four memory ranges that all get linked into the same segment: BRAND, RODATA, KERNEL and VECTORS. The linker puts them into the same address space, in the segment called "KERNEL". I just choose to use my own segment names instead of the ld65 defaults. When you see "KERNEL", think "CODE", so my examples might be a bit confusing.
I also split the NES's 2K of ram into different memory regions, and I use the linker config file to assign these memory regions to absolute addresses. QSHIELD is a 128 byte buffer of tiles to be blitted to the name-table, 2 columns 16 bytes tall, every NMI (the entire translated QS is stored in ram). NZONE contains the neutral zone tiles that are to be blitted to the name-table, 2 columns 30 tiles tall, every NMI (but only two columns are stored in RAM). I didn't have to do it this way.
I'm showing this because I think that it help illustrate the flexibility of what the linker can do for you.
Code: Select all
# NesYar/src/linker.cfg
# http://www.cc65.org/doc/ld65-5.html
MEMORY {
ZP: start = $0000, size = $0100, type = rw, define = no;
OAMBUF: start = $0200, size = $0100, type = rw, define = no;
QSHIELD: start = $0300, size = $0080, type = rw, define = no;
NZONE: start = $0380, size = $0020, type = rw, define = no;
RAM: start = $0400, size = $0400, type = rw, define = no;
HEADER: start = $0000, size = $0010, file = %O, fill = yes, define = no;
BRAND: start = $c000, size = $0100, file = %O, fill = yes, define = no, fillval = $00;
KERNEL: start = $c100, size = $3ef6, file = %O, fill = yes, define = no, fillval = $00;
VECTORS: start = $fff6, size = $000a, file = %O, fill = yes, define = no;
CHARROM: start = $0000, size = $2000, file = %O, fill = no, define = no;
}
SEGMENTS {
HEADER: load = HEADER, type = ro, define = no;
BRAND: load = BRAND, type = ro, define = no;
RODATA: load = KERNEL, type = ro, define = no, align = $100;
KERNEL: load = KERNEL, type = ro, define = no;
VECTORS: load = VECTORS, type = ro, define = no;
CHARROM: load = CHARROM, type = ro, define = no;
ZEROPAGE: load = ZP, type = zp, optional = yes, align = $100;
OAMBUF: load = OAMBUF, type = bss, define = no;
DATA: load = RAM, type = bss, define = no, align= $100;
NZONE: load = NZONE, type = bss, define = no;
QSHIELD: load = QSHIELD, type = bss, define = no;
}
FILES {
%O: format = bin;
}
btw, the 6502 only has 3 vectors. You'll notice that my linker config allocated 10 bytes instead of 6. I use the extra four bytes to store the assembler time stamp of when the build was made, thusly:
Code: Select all
.segment "VECTORS"
build_ts:
.dword .time
.word sys_nmi
.word sys_reset
.word sys_irq
Code: Select all
.segment "CHARROM"
.incbin "src/charrom.chr", 0, 8192
Code: Select all
.segment "HEADER"
.byte $4e,$45,$53,$1a ; header magic
.byte $01 ; # 16K prog roms
.byte $01 ; # 8K char roms
.byte %00000001 ; Vertical mirroring, NROM, no SRAM
.byte %00000000 ; NES mapper upper nibble
.byte 0,0,0,0,0,0,0,0 ; filler
I just noticed that in my 2008 MMC1 example, I defined the "ROM_x_MARKERS" in the "MEMORY" section of the linker (this is used when doing "address fixups" when emitting the actual binary.
But I never used them in the "SEGMENTS", so the linker never should have emitted those magic bytes to the binary. Either I mis-remembered something and have given you a bad example, or my example is slightly flawed.
Either way, I hope that my exmaples help you understand the relationship between using ".segment" inside your asm source, and what the linker can do with those segments.
But I never used them in the "SEGMENTS", so the linker never should have emitted those magic bytes to the binary. Either I mis-remembered something and have given you a bad example, or my example is slightly flawed.
Either way, I hope that my exmaples help you understand the relationship between using ".segment" inside your asm source, and what the linker can do with those segments.
Ok, but how do I decide what to put in each segment? How do I decide when to start a new segment? How do I decide what to put in each bank? How do I decide how large I want each rombank to be? How do I decide how many of them I want?You will probably have many code segments. One in each bank, at least.
I know what RAM and ROM is, but what does this mean?Note that my MMC1 example uses CHAR-RAM, so you don't see a CHAR-ROM in the SEGMENTS or MEMORY tables.
But how do we tell the linker that they're just RAM? Is that what the """type = rw""" signifies?The linker section "SEGMENTS" defines the order that the segments are layed out in the ROM file. That is why "HEADER" is first. "ZEROPAGE", "PLAYER" and "DATA" all define RAM, so those are not stored in the final binary. However, the linker needs to know about them so that it can assign addresses and do the "address fixups" when emitting the actual binary.
More Questions:
1) What is "char rom" supposed to stand for anyways? "character (font) rom"?
2) Is the
Code: Select all
101 FILES {
102 %O: format = bin;
103 }3) What is "bss"?
FinalZero wrote:I know what RAM and ROM is, but what does this mean?
NES carts have at least 2 memory chips: one for the program (always ROM) and one for tiles/characters (can be ROM or RAM). If the CHR is ROM, when you assemble the NES file you have to put its contents at the end of the file, but if the CHR is RAM, the NES file doesn't have any tiles at the end. The tiles will instead be stored somewhere in the PRG-ROM (wherever you want, in whatever format you want, they can even be compressed to save space) and the program itself will have to copy/decompress them to CHR-RAM using $2006/$2007.1) What is "char rom" supposed to stand for anyways? "character (font) rom"?
Sorry if I can't help with the assembler-specific stuff.
The size of a segment is dictated by the mapper that you are using. For MMC1 they will be 16K or 32K.FinalZero wrote:Ok, but how do I decide what to put in each segment? How do I decide when to start a new segment? How do I decide what to put in each bank? How do I decide how large I want each rombank to be? How do I decide how many of them I want?You will probably have many code segments. One in each bank, at least.
http://kevtris.org/mappers/mmc1/index.html
As the developer, you get to decide what you want to put where. The only "requirements" are
1) 6502 vectors (last 6 bytes of CPU address space) are mapped in when they could be invoked (and for reset, that is anytime), and that they point to the correct executable code.
2) If you want to use DCPM, your samples should be mapped into the CPUs address space. I've not experimented with DCPM yet, so I don't know the specifics.
I've read that the Legand of Zelda uses the first bank for its sound driver and music data.
I know that Crystalis (MMC3) uses its first few banks for storing all of the game's map data.
The physical cart needs to provide the PPU with 8K of addresses that it can fetch from. This can be ROM or RAM (or rarely, a combination of both).FinalZero wrote:I know what RAM and ROM is, but what does this mean?Note that my MMC1 example uses CHAR-RAM, so you don't see a CHAR-ROM in the SEGMENTS or MEMORY tables.
Crystalis uses multiple char-rom banks, and switches parts of them every few frames to achieve background animation, like waves crashing on the shore-line, or tall grasses waving in the wind.
Final Fantasy (MMC1) uses char-ram. I would speculate that the variety of monster party encounters rules out the possibility of storing all of the monsters in one or two char-rom banks. Plus the over-world map trick (B-Select when on the overworld) is done using char-ram, it is freaking cool!
I think so. I don't recall, I've not had to edit my linker config in a while.FinalZero wrote:But how do we tell the linker that they're just RAM? Is that what the """type = rw""" signifies?The linker section "SEGMENTS" defines the order that the segments are layed out in the ROM file. That is why "HEADER" is first. "ZEROPAGE", "PLAYER" and "DATA" all define RAM, so those are not stored in the final binary. However, the linker needs to know about them so that it can assign addresses and do the "address fixups" when emitting the actual binary.
1) Yes. (CHAR|PROG) = Character (PPU) / Program (CPU) address space.FinalZero wrote: More Questions:
1) What is "char rom" supposed to stand for anyways? "character (font) rom"?
2) Is thepart really needed? Doesn't it do that by default?Code: Select all
101 FILES { 102 %O: format = bin; 103 }
3) What is "bss"?
2) Yes, it is redundant. It was required by the redundant department of redundencies, and my pedantic need to explicitly state what I want in config file, and possibly by the voices in my cat's mind. Seriously, I don't know why I have it like that. I probably had specified other options there in the past, and then removed them, but left that section of the linker config.
3) The Wikipedia page can better articulate it than I can. Basically, BSS contains all non-heap global "data" that can changed at run-time. Memory reserved for BSS is typically NOT stored in a binary, and the process's "crt0" code (the stuff that the compiler vendor wrote, it calls your "main()") will initialize it to 0x00 for you before invoking main.
http://en.wikipedia.org/wiki/.bss
I have no idea what this is.DCPM
But what does this mean?The physical cart needs to provide the PPU with 8K of addresses that it can fetch from.
So what do they do then?I would speculate that the variety of monster party encounters rules out the possibility of storing all of the monsters in one or two char-rom banks.
... I don't think I understand the difference between a bank and a segment. Is a bank just part of a segment?
Ok, I understand bss in the context of C, but how does it relate to 6502 asm? Do all the variables have to stored there or something?3) The Wikipedia page can better articulate it than I can. Basically, BSS contains all non-heap global "data" that can changed at run-time. Memory reserved for BSS is typically NOT stored in a binary, and the process's "crt0" code (the stuff that the compiler vendor wrote, it calls your "main()") will initialize it to 0x00 for you before invoking main.
Me, misspelling DPCM. Delta Pulse Code Modulation. It is how the NES can play raw sound samples. The APU uses DMA to pull the samples from the high-end of the CPUs address space.FinalZero wrote:I have no idea what this is.DCPM
The PPU uses two 4K banks, one for background tiles, one for sprites. The PPU can be configured to pull tiles and sprites from the same 4K bank, or from either bank. Together, the PPU "sees" 8K ($0000 to $1fff in the PPU's address space). This memory lives on a cart as ROM or RAM and is called "CHAR-xxx". Physically, on the bus between the NES and the cart, the PPU controls 13 address lines (2^13 = 8192 = 8K bytes (well, technically, the ISO unit is "KiB", not "KB", but we'll ignore that here)) and reads 8 data bits. There are other control signals (RW, /CE) used to tell the memory circuit if the PPU wants to read or write, and "enable" the chip or not.FinalZero wrote:But what does this mean?The physical cart needs to provide the PPU with 8K of addresses that it can fetch from.
I'm sorry. I don't understand your question. What does _what_ do? I suppose that my example won't make any sense to you if you have not played FF1.FinalZero wrote:So what do they do then?I would speculate that the variety of monster party encounters rules out the possibility of storing all of the monsters in one or two char-rom banks.
Its all semantics. The cc65 compiler suite uses the term "segment", which can be of arbitary length. The NES simply has address ranges and two buses (CPU and PPU). The term "bank switching" means that a memory controller (ie, mapper chip) can swap memory out from underneath the CPU. Banks are typically large, and have sizes that are exact powers of 2, and are aligned on memory address boundaries that are powers of 2.FinalZero wrote: ... I don't think I understand the difference between a bank and a segment. Is a bank just part of a segment?
In a modern computer, the programs "data segment" is copied to ram. "RODATA" and "TEXT" (an archaic term for "code") are marked read-only (if the cpu's mmu supports that). "Data" is loaded just above the end of the "text", and BSS above that. The app's heap starts above BSS and grows up, and the stack starts at the end of the processes address space and grows down. When the heap and stack collide, bad shit happens.FinalZero wrote:Ok, I understand bss in the context of C, but how does it relate to 6502 asm? Do all the variables have to stored there or something?3) The Wikipedia page can better articulate it than I can. Basically, BSS contains all non-heap global "data" that can changed at run-time. Memory reserved for BSS is typically NOT stored in a binary, and the process's "crt0" code (the stuff that the compiler vendor wrote, it calls your "main()") will initialize it to 0x00 for you before invoking main.
In the NES is bit different. There is no "program loader" that places different segments into memory. Your reset routine _IS_ the loader, so to speak. Typically, a NES program will contain CODE and RODATA in ROM; use all RAM as BSS (but its not called that) and contain no traditional "DATA" segment. However, there is nothing to stop you from emitting a large chunk of "DATA" into your ROM and then copying it into RAM, where it can then be modified and treated as "DATA" like a C program executing on Unix would.