Jump to content
Sign in to follow this  
Andrew Davie

Session 5: Memory Architecture

Recommended Posts

Let's have a look at the memory architecture of the '2600, and how the 6502 communicates with the TIA and other parts of the '2600 hardware.

 

The 6502 communicates with the TIA by writing, and sometimes reading values to/from TIA 'registers'. These registers are 'mapped' to certain fixed addresses in the 6502's addressing range.

 

In its simplest form, the 6502 is able to address 65536 (2^16) bytes of memory, each with a unique address. Each 16-bit address ultimately directly controls the 'wires' on a 16-bit bus (=pathway) to memory, selecting the appropriate byte of memory to read/write. However, the '2600 CPU, the 6507, is only able to directly access 2^13 bytes (8192 bytes) of memory. That is, only 13 of the 16 address lines are actually connected to physical memory.

 

This is our first introduction to 'memory mapping' and mirroring. Given that the 6507 can only access addresses using the low 13 bits of an address, what happens if bit 14, 15, or 16 of an address are set? Where does the 6507 go to look for its data? In fact, bits 14,15, and 16 are totally ignored - only the low 13 bits are used to identify the address of the byte to read/write. Consider the valid addresses which can be formed with just 13 bits of data...

 

from %0000000000000 to %1111111111111

= from $0000 to $1FFF

 

Note: $0000 is the same as 0 is the same as %000 is the same as %0000000000. 0 is 0. In the same vein, any number with leading zeros is the same as that number without zeros. I often see people writing $02 when they could just write $2, or better yet... 2. Your assembler doesn't care how numbers are written. It's the value of numbers that matter. So use the most readable form of numbers, where it makes sense. Remember, 0 is 0000 is %0 is $000

 

So we've just written down the minimum and maximum addresses that can be formed with 13 bits. This gives us our memory 'footprint' - the absolute extremes of memory which can be accessed by the 6507 through a 13-bit address.

 

This next idea is important, so make sure you understand! All communication between the CPU and hardware (be it ROM, RAM, I/O, the TIA, or other) is through reads and/or writes to memory locations. Read that again.

 

The consequences of this are that some of that memory range (between $0 and $1FFF) must contain our RAM, some must contain our ROM (program), and some must presumably allow us to communicate with the TIA and whatever other communication/control systems the machine has. And that's exactly how it works.

 

We have just 128 bytes of RAM on the '2600. That RAM 'lives' at addresses $80 - $FF. It's always there, so any write to location $80 (128 decimal) will actually be to the first byte of RAM. Likewise, any read from those locations is actually reading from RAM.

 

So we've just learned that the 6507 addresses memory using 13 bits to uniquely identify the memory location, and that some areas of that memory 'range' are devoted to different uses. The area from $80 to $FF is our 128 bytes of RAM!

 

Don't worry too much about understanding this yet, but TIA registers are mapped in the memory addresses 0 to $7F, RIOT (a bit of '2600 hardware we'll look at later) from $280 - $2FF (roughly), and our program is mapped into address range $1000 to $1FFF (a 4K size).

 

Note: 1K = 1024 bytes = $400 bytes = %10000000000 bytes.

 

In essence, then, to change the state of the TIA we just have to write values to TIA 'registers' which look to the 6507 just like any other memory location and which 'live' in addresses 0 to $7F. To the 6502 (and I'll revert to that name now we've emphasised that the 6507 only has 13 address lines as opposed to the 6502's 16 and all other things are equal) a read or write of a TIA register is just the same as a read or write to any other area of memory. The difference is, the TIA is 'watching' those locations, and when you write to that memory, you're really changing the TIA 'registers' - and potentially changing what it draws on a scanline.

 

So now we know how to communicate with the TIA, and where it 'lives' in our memory footprint. And we know how to communicate with RAM, and where it 'lives'. Even our program in ROM is really just another area in our memory 'map' - the program that runs from a cartridge is accessed by the 6502 just by reading memory locations. In effect, the cartridge 'plugs-in' to the 6502 memory map. Let's have a quick look at what we know so far about memory...

 

Address Range Function

 

$0000 - $007F TIA registers

$0080 - $00FF RAM

$0200 - $02FF RIOT registers

$1000 - $1FFF ROM

 

We'll keep it simple for now - though you may be wondering what 'lives' in the gaps in that map, between the bits we know about. The short answer is 'not much' - so let's not worry about those areas for now. Just remember that when we're accessing TIA registers, we're really accessing memory from 0 to $7F, and when we access RAM, we're accessing memory from $80 to $FF, etc.

 

Now that we understand HOW the 6502 communicates with the TIA, one of our next steps will be to start to examine the registers of the TIA and what happens when you modify them. It won't be long now before we start to understand how it all works. Stay tuned.

 

I might give up writing "next time we'll talk about..." because I seem to end up covering something completely different.

Share this post


Link to post
Share on other sites

I'd like some feedback on this. How many people are following this, and how many of those want more?

Share this post


Link to post
Share on other sites
Surprisingly, it doesn't seem quite as overwhelming now as I at first thought!  The +hard+ part would be actually coming up with an original game concept that people would want to play.

 

Maybe I'm just a very good teacher. :)

Share this post


Link to post
Share on other sites

I'm following this and learning more from this than perusing source code and trying to figure out just what everything means. Trying to learn from just the source code and the stella guide is like looking at an electronics schematic and trying to build a television. It might be ok for some, but this method is much more effective at least for me.

 

Being a programmer helps, of course going from Java to assembly requires a slight shift in philosophy :D

 

With that said, more please! 8)

Share this post


Link to post
Share on other sites

I've only had time to read the first installment so far, but I definitely am interested, intend to follow along, and last night printed out the then-current thread in case something happened to it.

 

Keep it up!

 

-- Steve

Share this post


Link to post
Share on other sites
I'd like some feedback on this.  How many people are following this, and how many of those want more?

 

Hey! I'm in!

I find the course very good, it even makes me want to start

coding on the 2600 (!)

Only thing I am missing is pictures (drawings, schemas, pics) to

make things even more understandable. BTW, I found a very

interesting link about how TV works, and I'd like to share

it with the class:

 

http://www.howstuffworks.com/tv.htm

 

Regards

 

PS: Andrew, any possibility to make a webpage with this course?

Maybe hosted by AA?

Share this post


Link to post
Share on other sites

A memory map Diagram would be nice ;)

 

Question:

If the rom is addressed from $1000 - $1FFF

why do we set the code as ORG $0000 - $FFFA ?

Share this post


Link to post
Share on other sites
If the rom is addressed from  $1000 - $1FFF  

why do we set the code as ORG $0000 -  $FFFA ?

 

In Andrew's sample kernel he uses ORG $F000 to set the start of the code. He then uses ORG $FFFA to set the start of the 3 interrupt vectors. Each vector is 2 bytes, for a total of 6 bytes. $FFFA + 6 = $10000. $10000 - $F000 = $1000 = 4096 = 4K.

 

Now that I have that clarified I can answer your question. If you recall, the 6507 used in the 2600 only has 13 address lines. Thus from the 6507's perspective $F000 is the same as $1000 because the 13 least significant bits are the same. And when DASM generates the code (with the -f3 option) it starts the binary file at the first ORG. So DASM only generates a 4K file.

 

It really comes down to (for 4K games at least) style. Though if you want to use a 6502 emulator (instead of a 2600 emulator) you will need to put the interrupt vectors down at $FFFA (instead of $1FFA) so the emulator can find them, since the 6502 emulator will track all 16 bits of the address.

 

Clear as mud, right?

Share this post


Link to post
Share on other sites

i am setting up all the lessons in anther program so i can have a hardcopy to flip through and study as i stumble along.

 

please keep the info pouring in!

 

this is great!

Share this post


Link to post
Share on other sites

just wanted to pop in my two cents, great stuff, soaking in the knowledge of the master, learning. i -WILL- program a 2600 game one day, and andy may just be THE man who made it possible. :)

Share this post


Link to post
Share on other sites
A memory map Diagram would be nice ;)

 

Atari 2600 Memory Map:

----------------------

$0000-002F TIA Primary Image

$0030-005F [shadow] TIA

$0060-007F [shadow-partial] TIA

$0080-00FF 128 bytes of RAM Primary Image (zero page image)

$0100-002F [shadow] TIA

$0130-005F [shadow] TIA

$0160-017F [shadow-partial] TIA

$0180-01FF [shadow] 128 bytes of RAM (CPU stack image)

$0200-022F [shadow] TIA

$0230-025F [shadow] TIA

$0260-027F [shadow-partial] TIA

$0280-029F 6532-PIA I/O ports and timer Primary image

$02A0-02BF [shadow] 6532-PIA

$02C0-02DF [shadow] 6532-PIA

$02D0-02FF [shadow] 6532-PIA

$0300-032F [shadow] TIA

$0330-035F [shadow] TIA

$0360-037F [shadow-partial] TIA

$0380-039F [shadow] 6532-PIA

$03A0-03BF [shadow] 6532-PIA

$03C0-03DF [shadow] 6532-PIA

$03E0-03FF [shadow] 6532-PIA

$0400-07FF [shadow] Repeat the pattern from $0000-03FF

$0800-0BFF [shadow] Repeat the pattern from $0000-03FF

$0C00-0FFF [shadow] Repeat the pattern from $0000-03FF

 

$1000-17FF Lower 2K Cartridge ROM (4K carts start here)

$1800-1FFF Upper 2K Cartridge ROM (2K carts go here)

Share this post


Link to post
Share on other sites

I'm MUCH MUCH interested. I know this topic is almost a year old, but new buffoons like me are coming along and finding this a treasure trove of knowledge.

 

There is not enough praise for what this is and how simple it is to understand. I'm starting to get a bit hazy on some of it, but I'm still with you and I think a couple of rereads will clear things up. :) :) :) :)

Share this post


Link to post
Share on other sites

I just wanted to say I too am a late comer to this thread but I am learning a lot. I'd be willing to generate some visual diagrams of memory maps and what not if someone would check it for accuracy. I am a visual person and sometimes it is the only way to get visual people to understand concepts.

Thank for all your hard work Andrew.

Also, if you'd like to post this course to a permanent location I'd donate some web space on my site.

Mark

Share this post


Link to post
Share on other sites

Since I'm not the only late-comer, I'll chime in too. Great reading! I've done a small amount of assembly coding for the Atari 8-bit line of computers, but nothing on the 2600. Might be cool to try and at least get a static display going.

 

Stephen Anderson

Share this post


Link to post
Share on other sites
$0030-005F  [shadow] TIA

 

If those ram locations are just shadows, could I perform writes to them if I wanted to waste a given amount of cycles without affecting anything? i.e. if it's known that X < #$30, DEC $30,X or STA $30,X would be a handy 2-byte time-waster :)

Share this post


Link to post
Share on other sites
If those ram locations are just shadows, could I perform writes to them [...] without affecting anything?

 

no, of course not. shadowing means that certain parts (be it ram or hardware registers) appear at several places in the address space, due to incomplete address decoding.

 

so writing for instance to 0x30 has the same effect as writing to 0x00 (both locations are the VSYNC register).

Share this post


Link to post
Share on other sites
If those ram locations are just shadows, could I perform writes to them if I wanted to waste a given amount of cycles without affecting anything?  i.e. if it's known that X < #$30, DEC $30,X or STA $30,X would be a handy 2-byte time-waster :)

Nope, but there are some unused adresses ($2d..$2f) at the end of the TIA adress range ($00..$2f).

Share this post


Link to post
Share on other sites
Unused?  You mean they won't hold a value at all?

Most TIA registers don't hold a value because they are write-only and the remaining ones often only use a few bits.

Share this post


Link to post
Share on other sites

Hey, all, this is awesome.

 

And speaking of late-comers. I'm reading this in the middle of 2005! I have so much catching up to do...must...fight...urge...to...call...in...sick...

 

Also, a big thanks to kisrael for puting them into an outline. I almost gave up until I found that.

 

-Gota Run,

-C.F.

Share this post


Link to post
Share on other sites
so writing for instance to 0x30 has the same effect as writing to 0x00 (both locations are the VSYNC register).

575204[/snapback]

 

I wouldn't be terribly surprised if $0030-$003F map back to $0000-$000F, but I would expect that $0040-$005F would map to $0000-$001F [rather than to $0010-$002F as your diagram would suggest]

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

Loading...
Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.

×
×
  • Create New...