Page 1 of 2
STA indirect indexed double-increments PPU address?
Posted: Tue Oct 16, 2012 1:42 am
by rainwarrior
I was playing around with cc65, and I noticed that something like the following:
Compiles into to something equivalent to:
Code: Select all
LDX #$20
STX $11
LDX #$00
STX $10
LDY #$07
STA ($10), Y
This example is simplified, it isn't exactly what you get from cc65, but it seems cc65's [] operator in this case results in an indexed indirect store similar to this one.
What I discovered, though, is that across all the emulators I tried, STA to $2007 via an indirect indexed address like this appears to increment the PPU write address by two, rather than just one. On the first increment, PPU memory is not written. On the second increment, my value in A is stored to the PPU. So... it skips the byte I was aiming for and writes the next one instead! What is it about indirect indexed addressing that causes this behaviour?
Anyhow, this is also a warning, I guess, that if you're going to use cc65, don't try to write memory mapped registers this way. (edit: see posts below for syntax that does not have this problem.)
Re: STA indirect indexed double-increments PPU address?
Posted: Tue Oct 16, 2012 1:48 am
by Shiru
I don't really understand, why ((unsigned char*)0)[0x2007] = a rather than *((unsigned char*)0x2007)=a ?
Re: STA indirect indexed double-increments PPU address?
Posted: Tue Oct 16, 2012 1:53 am
by rainwarrior
Ah! That works much better, thanks!
Generates:
And by the way, the reason I had done it the other way first was because of suggestion #12 in this cc65 doc:
http://www.cc65.org/doc/coding.html
I misapplied it... I suppose the advice is only for when wanting to add to a pointer as an index, not for using static addresses like this.
Re: STA indirect indexed double-increments PPU address?
Posted: Tue Oct 16, 2012 7:09 am
by tepples
Which then leads to header files looking like this:
Code: Select all
#define PPUCTRL (*(volatile unsigned char*)0x2000)
#define PPUMASK (*(volatile unsigned char*)0x2001)
#define PPUSTATUS (*(volatile unsigned char*)0x2002)
#define OAMADDR (*(volatile unsigned char*)0x2003)
#define OAM_DMA (*(volatile unsigned char*)0x4014)
#define PPUSCROLL (*(volatile unsigned char*)0x2005)
#define PPUADDR (*(volatile unsigned char*)0x2006)
#define PPUDATA (*(volatile unsigned char*)0x2007)
Re: STA indirect indexed double-increments PPU address?
Posted: Tue Oct 16, 2012 7:47 am
by thefox
tepples wrote:Which then leads to header files looking like this:
Code: Select all
#define PPUCTRL (*(volatile unsigned char*)0x2000)
#define PPUMASK (*(volatile unsigned char*)0x2001)
#define PPUSTATUS (*(volatile unsigned char*)0x2002)
#define OAMADDR (*(volatile unsigned char*)0x2003)
#define OAM_DMA (*(volatile unsigned char*)0x4014)
#define PPUSCROLL (*(volatile unsigned char*)0x2005)
#define PPUADDR (*(volatile unsigned char*)0x2006)
#define PPUDATA (*(volatile unsigned char*)0x2007)
I like to do this:
Code: Select all
struct _PPU {
byte ctrl;
byte mask;
byte const status;
byte oam_addr;
byte oam_data;
byte scroll;
byte addr;
byte data;
};
#define PPU ( *( struct _PPU volatile * )0x2000 )
(BTW, volatile has no effect in CC65 currently.)
Re: STA indirect indexed double-increments PPU address?
Posted: Tue Oct 16, 2012 8:19 am
by exdeath
It's doing exactly what you're telling it to. It's compiling array/pointer dereferencing and indexing arithmetic, so an indexed indirect addressing opcode is the most perfect output code.
Re: STA indirect indexed double-increments PPU address?
Posted: Tue Oct 16, 2012 8:23 am
by thefox
exdeath wrote:It's doing exactly what you're telling it to. It's compiling array/pointer dereferencing and indexing arithmetic, so an indexed indirect addressing opcode is the most perfect output code.
Most perfect? No way, it should/could know that the address is constant and optimize accordingly.
Re: STA indirect indexed double-increments PPU address?
Posted: Tue Oct 16, 2012 8:27 am
by exdeath
thefox wrote:exdeath wrote:It's doing exactly what you're telling it to. It's compiling array/pointer dereferencing and indexing arithmetic, so an indexed indirect addressing opcode is the most perfect output code.
Most perfect? No way, it should/could know that the address is constant and optimize accordingly.

Re: STA indirect indexed double-increments PPU address?
Posted: Tue Oct 16, 2012 8:28 am
by rainwarrior
Heh, mine is now just:
Code: Select all
#define RAW_BUS(x) (*(unsigned char*)(x))
Am I the only one who likes to use the registers by number instead of naming them?
With cc65's weak optimizer,
volatile isn't really capable of doing anything, but the sentiment is right, semantically.
How does the compiler like that struct, TheFox? Does it manage to reduce 0x2000 + offset at compile time? (Edit: apparently it does! Turns into the
STA $2007 it deserves.)
Also, nobody has any insight as to what is special about STA (zp), Y? That was the question I was most interested in. Why does it generate an extra increment?
Re: STA indirect indexed double-increments PPU address?
Posted: Tue Oct 16, 2012 8:37 am
by exdeath
rainwarrior wrote:Also, nobody has any insight as to what is special about STA (zp), Y? That was the question I was most interested in. Why does it generate an extra increment?
Yeah... not sure. Whats the 6502 bus doing on that opcode? Maybe the ZP accesses for the base address cause dummy bus accesses that confuse the PPU and toggle a false write and increment?
Re: STA indirect indexed double-increments PPU address?
Posted: Tue Oct 16, 2012 8:46 am
by Dwedit
STA (xx),Y adds a dummy read.
Code: Select all
1 PC R fetch opcode, increment PC
2 PC R fetch pointer address, increment PC
3 pointer R fetch effective address low
4 pointer+1 R fetch effective address high,
add Y to low byte of effective address
5 address+Y* R read from effective address,
fix high byte of effective address
6 address+Y W write to effective address
They did it this way in case they needed to fix up the high byte before performing a write, because they figured that reads wouldn't have side effects like writes would.
Re: STA indirect indexed double-increments PPU address?
Posted: Tue Oct 16, 2012 8:51 am
by rainwarrior
exdeath wrote:It's doing exactly what you're telling it to. It's compiling array/pointer dereferencing and indexing arithmetic, so an indexed indirect addressing opcode is the most perfect output code.
One of the primary advantages of C over assembly is that the compiler is able to pick from equivalent implementations of a statement, so that it can do "what's best" for the situation. There really isn't such a thing as "exactly what you tell it". That's an assembly programming concept, not a C concept.
Frankly I'm a little disturbed that cc65 isn't able to tell the difference between a static pointer and a variable in this case. It's really weird too, because if I create a named static array (via assembly/linker), it manages to reduce just fine into an absolute address. It's this strange case where ((unsigned char*)x) isn't treated as a static pointer with the [] operator. Not intuitive at all.
For instance if I do something like this:
Code: Select all
.segment "PPU_REGISTERS"
_ppu_register: .res 8
.export _ppu_register
I can get well behaved results from something like:
Code: Select all
extern unsigned char ppu_register[8];
ppu_register[7] = a;
It's only when using a number literal cast to an address that it has problems with []. As TheFox pointed out, you can cast it to a struct and it has no problem at all!
Re: STA indirect indexed double-increments PPU address?
Posted: Tue Oct 16, 2012 9:00 am
by rainwarrior
Thanks Dwedit. Is that copied from a reference somewhere? (I'd like to read it, if it exists.)
Re: STA indirect indexed double-increments PPU address?
Posted: Tue Oct 16, 2012 9:02 am
by Dwedit
It's from this file:
http://nesdev.com/6502_cpu.txt
Yeah, there's a bunch of files linked from the main page of the site. But this one looks like the best for knowing what the CPU is actually doing.
Re: STA indirect indexed double-increments PPU address?
Posted: Tue Oct 16, 2012 9:17 am
by thefox
exdeath wrote:thefox wrote:exdeath wrote:It's doing exactly what you're telling it to. It's compiling array/pointer dereferencing and indexing arithmetic, so an indexed indirect addressing opcode is the most perfect output code.
Most perfect? No way, it should/could know that the address is constant and optimize accordingly.

Wat.