Yeah I suppose I could have added it to your ZIP and re-uploaded the ZIP. But then there'd be two ZIPs floating around one with and one without.rainwarrior wrote:So, we can attach .nesproject files now, but then I realized I should probably just add it to the zip.
Minimal NES example using ca65
Moderator: Moderators
- cpow
- NESICIDE developer
- Posts: 1097
- Joined: Mon Oct 13, 2008 7:55 pm
- Location: Minneapolis, MN
- Contact:
Re: Minimal NES example using ca65
Re: Minimal NES example using ca65
Off topic:
There's one small feature that I really miss from WinRAR after switching to 7zip: when the Extract button is pressed, it doesn't automatically fill the destination path with the name of the package (with extension stripped), instead it defaults to extracting to the directory where the package is. Usually not a problem since I mostly use the context menu to extract stuff, but sometimes it's more natural to open the archive in the program (e.g. when the file has been downloaded with a browser).
There's one small feature that I really miss from WinRAR after switching to 7zip: when the Extract button is pressed, it doesn't automatically fill the destination path with the name of the package (with extension stripped), instead it defaults to extracting to the directory where the package is. Usually not a problem since I mostly use the context menu to extract stuff, but sometimes it's more natural to open the archive in the program (e.g. when the file has been downloaded with a browser).
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
Re: Minimal NES example using ca65
rainwarrior, thanks, this is super useful for someone getting started with ca65 on the NES.
I have a question, though. (Probably dumb, as I'm new to both the cc65 suite and 6502 programming.)
In example.cfg, you have:
Because CHR starts at $0000, doesn't this mean that the TILES segment (full of data from `.incbin "background.chr"', etc.) will overlap the zero page and the stack page ($0100 - $01FF), as well as the OAM and RAM areas (not to mention the iNES header)? Doesn't this mean that if I, say, push something onto the stack, I'll be overwriting random parts of the background tiles data? I must not understand how ld65 behaves when you declare overlapping memory areas... why not declare all of the memory areas as strictly disjoint regions? (looks like none of the examples in the ld65 docs have overlapping memory areas)
I have a question, though. (Probably dumb, as I'm new to both the cc65 suite and 6502 programming.)
In example.cfg, you have:
Code: Select all
MEMORY {
ZP: start = $00, size = $0100, type = rw, file = "";
OAM: start = $0200, size = $0100, type = rw, file = "";
RAM: start = $0300, size = $0500, type = rw, file = "";
HDR: start = $0000, size = $0010, type = ro, file = %O, fill = yes, fillval = $00;
PRG: start = $8000, size = $8000, type = ro, file = %O, fill = yes, fillval = $00;
CHR: start = $0000, size = $2000, type = ro, file = %O, fill = yes, fillval = $00;
}
Re: Minimal NES example using ca65
The NES has two completely unrelated notions of address: one is for the CPU, and has zero page, stack, other RAM, &c.
The other is for the PPU and (almost always) contains just tile data.
They both start at 0. They're just different 0s.
The other is for the PPU and (almost always) contains just tile data.
They both start at 0. They're just different 0s.
- rainwarrior
- Posts: 8734
- Joined: Sun Jan 22, 2012 12:03 pm
- Location: Canada
- Contact:
Re: Minimal NES example using ca65
As lidnariq said, in this case these are two different memory regions (CPU vs PPU), but I will also explain the linker.
Each line of the MEMORY section describes a block of memory space that can be used by a SEGMENT, and also may be output to a file. MEMORY regions will be output in the same order as specified.
Each line of the SEGMENTS section describes an allocation of space in one of the previously defined MEMORY blocks. A segment doesn't have to fill up all of any particular MEMORY region, and will be assigned space contiguously in the order listed, sometimes with padding if a start address is specified.
Assembly code may specify which SEGMENT to use with the .segment directive, and the SEGMENT will be filled contiguously in the order the code/data appears.
All that said, blocks in MEMORY can refer to memory regions in different spaces, like in this case, or even the same space. What it really controls is whether and where that region will appear in the output file. A space that represents RAM doesn't normally go in the file. In an iNES file, there is a 16 byte header (HDR), a PRG block (PRG), and a CHR block (CHR), so I have specified 3 MEMORY regions for these three parts of the file.
If you are doing banking, you will typically want to have one MEMORY region per bank. In this case, many of them will overlap the same address space, but what matters is they will go into the file as separate blocks, and you can use SEGMENTS to specify which bank things need to go into.
Each line of the MEMORY section describes a block of memory space that can be used by a SEGMENT, and also may be output to a file. MEMORY regions will be output in the same order as specified.
Each line of the SEGMENTS section describes an allocation of space in one of the previously defined MEMORY blocks. A segment doesn't have to fill up all of any particular MEMORY region, and will be assigned space contiguously in the order listed, sometimes with padding if a start address is specified.
Assembly code may specify which SEGMENT to use with the .segment directive, and the SEGMENT will be filled contiguously in the order the code/data appears.
All that said, blocks in MEMORY can refer to memory regions in different spaces, like in this case, or even the same space. What it really controls is whether and where that region will appear in the output file. A space that represents RAM doesn't normally go in the file. In an iNES file, there is a 16 byte header (HDR), a PRG block (PRG), and a CHR block (CHR), so I have specified 3 MEMORY regions for these three parts of the file.
If you are doing banking, you will typically want to have one MEMORY region per bank. In this case, many of them will overlap the same address space, but what matters is they will go into the file as separate blocks, and you can use SEGMENTS to specify which bank things need to go into.
Re: Minimal NES example using ca65
Thanks, guys, I think I get it now. What I failed to appreciate is that with the MEMORY areas that are being written to the file (%O), we are actually describing the layout of the iNES file, not the runtime layout of memory -- the iNES format has its own conventions about how its contents will determine the initial state of memory at power-on. And, on the other hand, the MEMORY areas with file = "" (ZP, OAM, RAM) are referring to the runtime address space, but this is for purposes of the symbolic assembler knowing how to interpret what we put in our source code, so it knows, e.g., that ".res 1" means to reserve a byte in one region if it appears under ".segment "ZP"", but in another region if it appears under ".segment "RAM"".
Re: Minimal NES example using ca65
One more question: is there a particular reason that you define the gamepad_poll subroutine in the DATA segment, rather than in the CODE segment? Changing it to CODE compiles and works fine. Is it more efficient somehow to have this subroutine located in DATA? (Not sure how that would be, unless you were depending on relative addressing in such a way that you wanted to be close to certain addresses, but I don't see how that's the case here.)
Re: Minimal NES example using ca65
Looks like a mistake. It's not more efficient.thenendo wrote:One more question: is there a particular reason that you define the gamepad_poll subroutine in the DATA segment, rather than in the CODE segment? Changing it to CODE compiles and works fine. Is it more efficient somehow to have this subroutine located in DATA? (Not sure how that would be, unless you were depending on relative addressing in such a way that you wanted to be close to certain addresses, but I don't see how that's the case here.)
In this case it doesn't matter which segment the code is placed in, because both the CODE and the DATA segments are ROM and end up in the same memory area. If one wanted to place code at certain addresses, the correct way to do it in cc65/ca65 would be to define a new segment (and possibly a new memory area as well) with the desired starting address.
...
On an unrelated note, it goes somewhat against convention to name the read-only data segment DATA. Typically RDATA or RODATA is used for that, and DATA is used for initialized data in RAM. Not saying that it should be changed, only noting it here since some other source code might be using different conventions.
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
- rainwarrior
- Posts: 8734
- Joined: Sun Jan 22, 2012 12:03 pm
- Location: Canada
- Contact:
Re: Minimal NES example using ca65
Yeah, just a mistake. I will correct that.
Also, that is a good point. I forgot that RODATA is the cc65 convention for read-only data, though the name might be a little more obscure to new users. Not sure if I want to correct this. The main reason I bother to use separate CODE and DATA segments is that it keeps them nicely separated (good for debugging disassembly if all the code is in one contiguous place) and so the map statistics the linker generates will list them separately.
Also, that is a good point. I forgot that RODATA is the cc65 convention for read-only data, though the name might be a little more obscure to new users. Not sure if I want to correct this. The main reason I bother to use separate CODE and DATA segments is that it keeps them nicely separated (good for debugging disassembly if all the code is in one contiguous place) and so the map statistics the linker generates will list them separately.
Re: Minimal NES example using ca65
Awesome, gotcha. While you're at it, I think you could also fix a typo in the comment on line 52 (byte 5 of the iNES header):
That should say 8k, right?
Code: Select all
.byte $01 ; 4k CHR bank count
- rainwarrior
- Posts: 8734
- Joined: Sun Jan 22, 2012 12:03 pm
- Location: Canada
- Contact:
Re: Minimal NES example using ca65
Relatedly, I think you might want to use "ZEROPAGE" instead of "ZP". Using your linker config (with "ZP"), I found that this code makes assembler fail with "Error: Range error":rainwarrior wrote: Also, that is a good point. I forgot that RODATA is the cc65 convention for read-only data, though the name might be a little more obscure to new users. Not sure if I want to correct this. The main reason I bother to use separate CODE and DATA segments is that it keeps them nicely separated (good for debugging disassembly if all the code is in one contiguous place) and so the map statistics the linker generates will list them separately.
Code: Select all
.segment "ZP"
addr0: .res 2
.segment "CODE"
; ... in some subroutine
sta addr0+1
Edit: It makes sense that ca65 isn't smart enough, because the assembler runs before the linker, so it has no knowledge of the segment layouts and types; so it's forced to rely on hard-coded conventions for certain things, like "ZEROPAGE".
Re: Minimal NES example using ca65
Your analysis is correct. However, it's also possible to explicitly specify a segment as a zero page segment:thenendo wrote:Edit: It makes sense that ca65 isn't smart enough, because the assembler runs before the linker, so it has no knowledge of the segment layouts and types; so it's forced to rely on hard-coded conventions for certain things, like "ZEROPAGE".
Code: Select all
.segment "ZP" : zeropage
foo: .res 1
; ...
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
- rainwarrior
- Posts: 8734
- Joined: Sun Jan 22, 2012 12:03 pm
- Location: Canada
- Contact:
Re: Minimal NES example using ca65
Ack, yeah I forgot about that special name too. Okay, I've renamed ZP > ZEROPAGE, and DATA > RODATA to keep with ca65 convention.thenendo wrote:rainwarrior wrote:Relatedly, I think you might want to use "ZEROPAGE" instead of "ZP". Using your linker config (with "ZP")
- rainwarrior
- Posts: 8734
- Joined: Sun Jan 22, 2012 12:03 pm
- Location: Canada
- Contact:
Re: Minimal NES example using ca65
Fixed a bug with the emphasis bits, accidentally had emphasize-red set on.