Accessing RAM

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
67726e
Posts: 129
Joined: Sat Apr 03, 2010 5:45 pm
Location: South Carolina
Contact:

Accessing RAM

Post by 67726e »

I have learned a majority of my 6502 programming from Nerdy Nights which uses the NESASM3 compiler. NESASM3 uses .rsset to start the RAM location and .rs to set aside locations:

Code: Select all

  .rsset $0000
Var .rs 1 ; Reserve one byte ($0000)
This is NESASM's way of working with RAM via a macro. What is the 6502 way to do this?
User avatar
Dwedit
Posts: 4470
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Post by Dwedit »

This question has no meaning. The 6502 has no concept of labels, just the instructions and addresses themselves. It's completely dependent on the assembler you are using. Which assembler?
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!
User avatar
67726e
Posts: 129
Joined: Sat Apr 03, 2010 5:45 pm
Location: South Carolina
Contact:

Post by 67726e »

Perhaps I didn't word my question correctly? What I am trying to say is, that is how NESASM3 allows a person to code access to the RAM via the use of a built-in macro.

Using pure 6502 assembly, how would one write the code to access addresses in the RAM?
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

To store the number five at address $80:

Code: Select all

  lda #5
  sta $80
To do the same thing with a label:

Code: Select all

lives = $80
STARTING_LIVES = 5
; ...
  lda #STARTING_LIVES
  sta lives
And to do the same thing with reserving storage under ca65 (NESASM appears similar in this respect):

Code: Select all

STARTING_LIVES = 5
.segment "ZEROPAGE"
lives: .res 1

.segment "CODE"
; ...
  lda #STARTING_LIVES
  sta lives
User avatar
Dwedit
Posts: 4470
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Post by Dwedit »

To access the RAM, you have instructions like LDA $20 or LDX $0654. They have the address right inside the instruction.

I have no idea what you mean when you say "Pure 6502 assembly". There are assemblers which let you use labels and equates, and there's the binary machine code itself which has no idea what a label is, just memory addresses.

Some assemblers like you to use stuff like MyLabel .equ $20, then LDA MyLabel. Other assemblers use the = directive to declare labels. ASM6 lets you define a bunch of labels in RAM by using the ENUM directive.

This is all specific to which assembler tool you are using to assemble your code. Just saying 6502 doesn't indicate any particular assembler.
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!
User avatar
67726e
Posts: 129
Joined: Sat Apr 03, 2010 5:45 pm
Location: South Carolina
Contact:

Post by 67726e »

Tepples has what I was seeking. So correct me if I'm wrong here, as far as writing to the RAM goes, you can write anywhere between $0000-$0800 which would be the NES' 2K worth of RAM? If I have WRAM in my game, then I could also use $6000-$8000? Am I in the ballpark here? Anything missing?
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

Dwedit wrote:I have no idea what you mean when you say "Pure 6502 assembly". There are assemblers which let you use labels and equates, and there's the binary machine code itself which has no idea what a label is, just memory addresses.
I'm guessing it has something to do with bare-bones dialects of assembly language used by interactive assemblers such as another platform's debug.exe and the Apple II mini-assembler, or the sort of assembly language you get from the disassembler built into Nintendulator or FCEUX. These dialects tend to lack labels.

As I interpret the question, it's "what do these labels do?"

EDIT: I was right. I'll edit in an answer to the new question:
67726e wrote:as far as writing to the RAM goes, you can write anywhere between $0000-$0800 which would be the NES' 2K worth of RAM? If I have WRAM in my game, then I could also use $6000-$8000? Am I in the ballpark here? Anything missing?
Addresses $0200-$07FF are free, and $6000-$7FFF are also free if you have PRG RAM on your cartridge board. Addresses $0000-$01FF have special meanings, but you can put variables in unused parts of those. Shall I explain how my games use memory?
Last edited by tepples on Tue Nov 30, 2010 6:19 pm, edited 1 time in total.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

Like it's been said, the CPU itself has no idea what a label is. It just sees addresses and numbers, nothing more. Assemblers were invented to make our lives easier, because they allow us to use names and expressions, which are easier to understand and keep track of than raw numbers, and when you assemble the source file(s) they generate all those addresses and numbers for you.

If you want to code in "pure 6502" you are gonna need an hex editor, and you are gonna have to input all the operations using their codes instead of their names (i.e. $20 instead of RTS - and the code changes depending on the addressing mode: there are 8 different codes for LDA, for example) and all values and addresses as plain numbers. It will take you forever to build the simplest of the programs.
User avatar
67726e
Posts: 129
Joined: Sat Apr 03, 2010 5:45 pm
Location: South Carolina
Contact:

Post by 67726e »

What I mean with 'pure 6502' was just the bare mnemonics/operands i.e.

Code: Select all

LDA $0200
As opposed to

Code: Select all

LDA Some_Label
I didn't mean writing the actual opcodes and values.
Addresses $0000-$01FF have special meanings, but you can put variables in unused parts of those.
How exactly would one know where the unused slots are? The information that I have on the '.rsset' directive with NESASM3 allows you to start the addressing at $0000 and work your way up from there so wouldn't that have the potential to screw with the 'special meanings'

So for my sprites in-game, I have something like this:

Code: Select all

	LDX #$00
LoadSprite:
	LDA Sprite, x
	STA $0200, x
	INX
	CPX #$18
	BNE LoadSprite
To load the sprites into the RAM so it occupies $0200-$0217, now in NMI I have to put:

Code: Select all

	LDA #$00
	STA $2003
	LDA #$02
	STA $4014
I actually have two question regarding this:

1) How does the PPU know when to stop reading?
2) Why do I write the second byte of the address to $4014 instead of $2003?
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

67726e wrote:How exactly would one know where the unused slots are? The information that I have on the '.rsset' directive with NESASM3 allows you to start the addressing at $0000 and work your way up from there so wouldn't that have the potential to screw with the 'special meanings'
Instructions using indirection need to use addresses in $0000-$00FF, the 256-byte "zero page". Stack instructions (PHA, PLA, JSR, RTS, etc.) need to use addresses in $0100-$01FF, the 256-byte "stack page". Those are the only two areas with special meanings to the CPU, but rough conventions about the use of some other areas arose during the NES's commercial era. I just wrote a wiki article showing suggested uses for parts of RAM.
To load the sprites into the RAM so it occupies $0200-$0217, now in NMI I have to put:

Code: Select all

	LDA #$00
	STA $2003
	LDA #$02
	STA $4014
I actually have two question regarding this:

1) How does the PPU know when to stop reading?
The CPU's DMA unit always copies exactly 256 bytes from this page to the OAM data port on the PPU ($2004). Unused memory in this page should be cleared to values between $EF and $FF, so that unused sprites are hidden below the bottom of the visible area of the screen.
2) Why do I write the second byte of the address to $4014 instead of $2003?
You're writing the destination address, which is inside OAM, to PPU port $2003. (This address should always be set to zero unless you really know what you're doing.) You're writing the upper 8 bits of the source address, which is inside ordinary RAM, to $4014. The copy always starts at the first byte ($xx00) of this page.
User avatar
67726e
Posts: 129
Joined: Sat Apr 03, 2010 5:45 pm
Location: South Carolina
Contact:

Post by 67726e »

Once again, thank you for the help. I am sorry I wasn't all that clear about what I was seeking Dwedit.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

67726e wrote:What I mean with 'pure 6502' was just the bare mnemonics/operands i.e.

Code: Select all

LDA $0200
Keep in mind that if you plan on coding like this, it will be hell to move stuff around when you need to, and trust me, that will happen eventually. Say that you decide to use 64 bytes to keep track of a number of items available in a level, but later you decide that you want to have 128 items per level instead of just 64. What happens if you have used the space that comes after those 64 bytes for something else? You'll have to go through all of the code looking for the parts where you used those addresses and change them to something else. If you had named the variables, it would just be a matter of changing their declarations, and the whole program would use the variables in their new place.

Another advantage to naming your variables is that code gets easier to read. If you decide to store the number of lives at address $043F, a few days later you will already have forgotten that, and will probably be lost when looking at a "DEC $043F" instruction if you didn't write a comment indicating that this code is taking a life away. But if instead you wrote "DEC NumberOfLives", the code would be self-explanatory.
User avatar
67726e
Posts: 129
Joined: Sat Apr 03, 2010 5:45 pm
Location: South Carolina
Contact:

Post by 67726e »

Don't get me wrong. I use labels/variables for my code. Self-commenting code is the best kind after all but I was asking more for the sake of learning and understanding how things work rather than how I wish to code.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

Ah, I see. All you need to know right now is that deep down, the CPU can only deal with bits & bytes. Every command has a code, operands are either immediate values or addresses, and each operation takes a set amount of time to complete. All the shit offered by assemblers (labels, macros, and all kinds of directives) are there just to make our life easier, so that we don't have to keep track of addresses, offsets, or anything like that.

With time, as your programs get more complex and you start to debug them in emulators such as FCEUX you'll get a better understanding of the stack, zero page and so on, because you'll be able to observe how they work, step by step. Don't overestimate the 6502 though, it's a very straightforward CPU that follows a very specific set of rules.

Sometimes, descriptions like "Branch if EQual" might not make much sense, because sometimes you are not even comparing anything, so how could the CPU even consider something equal to or different from something else? But when you look at the underlying steps of the operation you discover that what it actually does is branch when the Z(ero) flag is set, so any operation that resulted in zero (even if it was just loading the value $00 into some register) will cause that branch to be taken, even if it's not a comparison. Lots of other mnemonics can be misleading (RTS for example: it doesn't necessarily returns, it just jumps to whatever address was put at the top of the stack, be it the place where the routine was called or not), but when you look at what they actually do, step by step, you will be able to make better use of them.
Post Reply