Vs System Shared Memory

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Vs System Shared Memory

Post by rainwarrior »

That issue (any many others) are a big reason why I suggest never doing any direct hardware access in C code. Anything that reads/writes a hardware register, or in this case, requires specific function of the underlying memory (a little less obvious case), should be done in assembly.

You can make a "POKE" macro, but there is no C safe way to specify the kind of 1:1 access you're expecting from it. It may work in all of your cases, but it's generally subject to a lot of compile whims. For safety's sake I'd suggest turning that into an assembly function to isolate it. Maybe something like:

Code: Select all

// C

extern void poke(void* addr, char value);
extern char peek(void* addr);

; assembly

.segment "DATA"

poke_finish:
	sta a:$FFFF ; address to be overwritten
	rts

peek_finish:
	lda a:$FFFF
	rts

.segment "CODE"

.export _poke
.export _peek
.import popax

_poke: ; A = value, C-stack = addr
	pha ; value
	jsr popax ; addr to X:A
	sta poke_finish+1
	stx poke_finish+2
	pla
	jmp poke_finish

_peek: ; X:A = addr
	sta peek_finish+1
	stx peek_finish+2
	ldx #0 ; cc65 functions with 8-bit return must leave 0 in X
	jmp peek_finish
Edit: added peek as well. Note the reason I used self modifying DATA is that lda/sta are pure instructions with no extra read/write side effects.
Last edited by rainwarrior on Fri Dec 11, 2020 2:13 pm, edited 1 time in total.
Fiskbit
Posts: 890
Joined: Sat Nov 18, 2017 9:15 pm

Re: Vs System Shared Memory

Post by Fiskbit »

This may be wrong, but I'd thought this issue is solved with the volatile keyword, telling the compiler not to cache the variable and that it should be reaccessed on each use.
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Vs System Shared Memory

Post by tepples »

The volatile keyword is exactly what the GBA homebrew scene uses to write to registers from C code.
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Vs System Shared Memory

Post by rainwarrior »

Fiskbit wrote: Wed Dec 09, 2020 7:51 pmThis may be wrong, but I'd thought this issue is solved with the volatile keyword, telling the compiler not to cache the variable and that it should be reaccessed on each use.
The volatile keyword was used for that historically in some C compilers, but it's use is limited and compiler-specific. In cc65, what it does is disable all optimization for the entire function wherever it is used, which doesn't actually solve all functional problems (e.g. it doesn't stop if from generating instructions with dummy-read/write side effects), and makes any variable using that qualifier a huge performance bomb.

So... sort of, but not a very good use in this situation. Moving it to an assembly function, even with the calling overhead, is still more performant than volatile in most cases, and completely safe rather than mostly.


EDIT: I was partially incorrect but the recommendation is the same (see later post below): volatile asm disables optimizations, but volatile variables do not. However, volatile variables are not reliably volatile in cc65.
Last edited by rainwarrior on Fri Dec 11, 2020 12:26 am, edited 1 time in total.
User avatar
Jarhmander
Formerly ~J-@D!~
Posts: 568
Joined: Sun Mar 12, 2006 12:36 am
Location: Rive nord de Montréal

Re: Vs System Shared Memory

Post by Jarhmander »

I developed professionally various embedded systems with different microcontrollers from various vendors (STMicroelectronics, Microchip, NXP, TI, ...), and I can guarantee that for all peripheral register definitions, they all use the volatile keyword for qualifying the storage, either with an extern declaration or through a macro that expand into a integer cast to a pointer to volatile data.
((λ (x) (x x)) (λ (x) (x x)))
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Vs System Shared Memory

Post by rainwarrior »

My advice was specifically for the Vs System (or NES) and cc65. Volatile gives you really, really terrible code quality on cc65, and sometimes doesn't actually safely solve the relevant problems.


EDIT: I was partially incorrect but the recommendation is the same (see later post below): volatile asm disables optimizations, but volatile variables do not. However, volatile variables are not reliably volatile in cc65.
Last edited by rainwarrior on Fri Dec 11, 2020 12:26 am, edited 1 time in total.
User avatar
aquasnake
Posts: 515
Joined: Fri Sep 13, 2019 11:22 pm

Re: Vs System Shared Memory

Post by aquasnake »

I have no offense

Rainwarrior is a staunch supporter of assembler. Even though he uses cc65, 95% of the code should be assembly, and the remaining 5% should be compilation control script, Python and makefile

I'm sure I'm a strong supporter of C. more than 70% of the code is C. Except for the loader(crt0.asm), handling of input recognition of joypad, and so on...

We all insist on our own ideas for the development model we are used to, but I would like to say that C is not inefficient, the quality of generated code is not bad, and the benefits and development convenience far exceed the sacrifice of even biased "performance"
lidnariq
Posts: 11430
Joined: Sun Apr 13, 2008 11:12 am

Re: Vs System Shared Memory

Post by lidnariq »

Rainwarrior's point appears to be about just how mediocre cc65 is. This isn't a "C" vs "asm" argument. On other architectures with more-functional C compilers, volatile is a fine solution. But not with cc65.
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Vs System Shared Memory

Post by rainwarrior »

aquasnake wrote: Thu Dec 10, 2020 6:45 amRainwarrior is a staunch supporter of assembler. Even though he uses cc65, 95% of the code should be assembly, and the remaining 5% should be compilation control script, Python and makefile...
ლ(ಠ益ಠლ) Could I ask you to stop just making up complete nonsense? I've spent far too much time on this forum literally defending the use of C for NES and trying to help people get better mileage out of cc65 to deserve this. I contribute to the maintenance of cc65 because I think it's a useful NES/6502 compiler!!! Go fly a kite.
User avatar
Goose2k
Posts: 320
Joined: Wed May 13, 2020 8:31 am
Contact:

Re: Vs System Shared Memory

Post by Goose2k »

lidnariq wrote: Tue Dec 08, 2020 12:31 pm
Is this because only the primary CPU can write that bit? Can CPU2 not trigger the IRQ on CPU1?
No? That works fine?
Sorry, but I don't understand your answer to this one. Let me try to clarify my question.

Which of the following is true:

a) Secondary CPU can trigger IRQ on Primary CPU by writing 0 to $4016.1.
b) Secondary CPU can not trigger IRQ on Primary CPU by writing 0 to 4016.1.

Apologies again for asking so many questions; as I mentioned I don't have the hardware myself, so I am working mostly blind. I really do appreciate your time!
rainwarrior wrote: Wed Dec 09, 2020 6:00 pm That issue (any many others) are a big reason why I suggest never doing any direct hardware access in C code. Anything that reads/writes a hardware register, or in this case, requires specific function of the underlying memory (a little less obvious case), should be done in assembly.

...
That's super helpful, thanks!
lidnariq
Posts: 11430
Joined: Sun Apr 13, 2008 11:12 am

Re: Vs System Shared Memory

Post by lidnariq »

Goose2k wrote: Thu Dec 10, 2020 4:08 pm Sorry, but I don't understand your answer to this one. Let me try to clarify my question.

Which of the following is true:

a) Secondary CPU can trigger IRQ on Primary CPU by writing 0 to $4016.1.
b) Secondary CPU can not trigger IRQ on Primary CPU by writing 0 to 4016.1.

Apologies again for asking so many questions; as I mentioned I don't have the hardware myself, so I am working mostly blind. I really do appreciate your time!
Both CPUs can trigger IRQs on the other CPU – at least, as long as the other CPU permits IRQs.

Only the primary CPU can control which CPU has access to shared RAM.

Right now, the wiki says
When 0, triggers an IRQ on the other CPU (if interrupts are enabled)
Is this unclear? Should I explicitly enumerate both directions?
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Vs System Shared Memory

Post by rainwarrior »

I'm wondering if it should not be called a "trigger", since that seems to suggest a one-shot effect.

When the /IRQ line on the 6502 is asserted (i.e. held low) the CPU will run its interrupt handler the next time it wants to start a new instruction (and has its interrupts enabled). The signal is not a one time thing, but held, because the device issuing the IRQ does not really know how long it will be before the IRQ actually gets serviced by the CPU.

This is also why they need some form of acknowledgement, where the CPU tells the IRQ device that it's doing the request now and it should turn off its request (/IRQ back to high). It seems unusual that this particular interface has no direct acknowledgement mechanism, but I pointed out a workaround for this in my example earlier in the thread.

As long as the /IRQ signal remains low, the CPU will just immediately interrupt again after the RTI instruction, over and over. It will keep happening unless the requester turns it off, or the CPU does something to set its own interrupt enable flag. Under normal circumstances, the beginning of the interrupt handler will have the flag set (so you can't interrupt again during the handling), but RTI returns the flags to their original state, clearing the flag and re-enabling interrupts.

So... maybe we should say "assert" rather than "trigger" since I think we need to realize that it's a continuous signal to successfully operate this one. The lack of an ack makes this one in particular a lot less like a one shot effect than most IRQ devices.
nocash
Posts: 1405
Joined: Fri Feb 24, 2012 12:09 pm
Contact:

Re: Vs System Shared Memory

Post by nocash »

It might be my bad english, but I am somehow translating "asserted" to "assumed to be true" or some other scientific sounding but meaningless geek stuff.
You have almost convinced me that "trigger" could be misleading, but on the other hand, it's common to say "edge triggered" as well as "level triggered".

Either way, a short warning about level triggering might helpful. And some flowchart, I think this would be easiest:

1. Primary Main Program does trigger IRQ request
2. Secondary IRQ handler reads & writes SRAM, then triggers a short IRQ request (for about a dozen cycles) (to notify that it's done with SRAM)
3. Primary IRQ handler does release IRQ

Both IRQ handlers may need an extra delay before trying to RTI, or some other mechanism to ignore re-triggered IRQs.
Last edited by nocash on Thu Dec 10, 2020 10:20 pm, edited 1 time in total.
homepage - patreon - you can think of a bit as a bottle that is either half full or half empty
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Vs System Shared Memory

Post by rainwarrior »

"Assert" is the standard verb for an interrupt request in most technical documentation I've seen, which is why I want to use it in the description.

I don't think the Vs System page is really the place to explain how IRQs are triggered differently than NMIs but probably we could use some good explanation of that somewhere on the wiki. (We might have one already? Maybe just needs a link...)

My objection to calling the mechanism through $4016 a "trigger" is that there's not a 1:1 correspondence between the action of writing $4016 and an IRQ response being triggered. One action can result in many IRQ responses or none. There's a layer of indirection here between what you're doing through this interface and what the CPU does. The interrupt response is triggered more directly by the CPU, all the Vs. mechanism is doing is making a request, not directly triggering it, so I think it may help to characterize it that way in its description.

An edge triggered IRQ is a little more direct, at least you do 1 action and there is only 1 IRQ response that comes from it. There's less to worry about there.
nocash
Posts: 1405
Joined: Fri Feb 24, 2012 12:09 pm
Contact:

Re: Vs System Shared Memory

Post by nocash »

Yeah, at least in the past some years. They are now asserting everything... not only interrupts... also chip selects and whatever.
To me it appears to be a relative new habit, perhaps derived from elite universities, or maybe from google translate.
How do other english speakers feel about it? Is legit and precise to "assert" something, or does it just sound stupid to you?

EDIT: How about "request" interrupt instead of "trigger" or "assert"?
It's implied anyways, "triggering IRQ" means "triggering the Interrupt ReQuest", not neccessarily same as actually triggering the Interrupt Execution(s).

With short warning I just meant something like "mind that the IRQ is level triggered", or a few more words, or a link to more info.
Last edited by nocash on Thu Dec 10, 2020 10:47 pm, edited 3 times in total.
homepage - patreon - you can think of a bit as a bottle that is either half full or half empty
Post Reply