Why NES have too ugly arch?

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: 8732
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Why NES have too ugly arch?

Post by rainwarrior »

Oziphantom wrote:Sure I could easily make my own standard but the standard set down by Chuck and Bill is the standard we all know and use. SEI Set Enable Interrupt - actually disables Interrupts. Not a 6502 design flaw, nothing wrong with the die, but still Chuck and Bill's misstep ;)
The early MOS datasheets for 6502 call them "Set Interrupt Disable Status" and "Clear Interrupt Disable Bit". I dunno where you got "Set Enable Interrupt" from, but if you were confused about the meaning of this mnemonic because of it, it wasn't from "Chuck and Bill".

(Edit: Okay apparently 3 other people said this already while I was typing.)
Oziphantom wrote:
tokumaru wrote:
JSR (XXXX) would save a lot of pain too.
Working with the attribute tables in an 8-way scroller is a pain. Changing the palette mid-frame is a pain. Doing raster effects without IRQs is a pain. Animating patterns in such a short vblank time is a pain. JSR'ing to a JMP (XXXX) is definitely not a pain.
Doing a push with the address -1 on the stack then doing a jump is cumbersome, and I come from a machine where I can actually just change the JSR params ;) Still it would be nice if I didn't have to. Also those other things are NES problems not a 6502 problem.
A substitute wrapper for JSR (XXXX) takes only one extra line of code.

Code: Select all

	jsr jsr_xxxx
...
jsr_xxxx:
	jmp ($XXXX)
The -1 problem is self inflicted complexity. You can use RTS for a jump table implementation, but there's not a whole lot of need to do so. It's only really "necessary" in some very specific situations (e.g. you want the pointer on the stack).
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Why NES have too ugly arch?

Post by tepples »

Oziphantom wrote:Personally I think getting SEI and CLI backwards was their biggest misstep on the 6502. Also Non-Maskable .. :roll: sigh...
Think of it this way and it'll click.

I bit is the minimum level of IRQs that get through.
NMI and RESET are level 1, and IRQ is level 0.
SEI sets the minimum level to 1, and NMI and RESET have level at least 1.
CLI sets the minimum level to 0, and IRQ, NMI, and RESET have level at least 0.
tokumaru wrote:What do you mean backwards? Do you think the mnemonics would better match their function if they were switched?
I think Oziphantom is referring to the fact that it's an IRQ inhibit bit in the first place. On some other architectures, the sense of the I bit are such that 1 means allow IRQ and 0 means suppress IRQ, such as 8086 and the otherwise very 65C02-like SPC700. Blargg's SPC700 macro pack for ca65 switches the mnemonics back around to the 6502 way, but the underlying 8086 way is still visible if you PHA PLP or PHP PLA.
tokumaru wrote:Even in the NMI handler, where I need to backup all 3 registers, I use 3 ZP locations I have reserved exclusively for this purpose, instead of using the stack.
And then you formally prove that NMI-in-NMI can never happen, right?
tokumaru wrote:LDA (ZP, X) can be useful for accessing collections of streams, such as the different channels of a song.
As seen in, for example, the Pently music engine. It stores pointers to note attack data, sound effect data, and note pattern data for each of the four APU channels, plus one more pointer to note pattern data for the attack track. But it might not appear so useful if your only 6502 experience is on 6502-based home computers such as Apple IIe and Commodore 64, where BASIC uses up most of zero page.
tokumaru wrote:Specially on the Atari 2600, which doesn't have any interrupts, so S can safely be used to load data faster in display kernels.
Or in my Popslide VRAM update kernel for NES, because there's enough space in page $01 to leave room for the interrupt handler before the update data.
za909 wrote:As far as instruction sets go, sometimes I look at the Z80 set with quite some envy.
I sure don't, particularly when it comes to random access to the properties of an entity. IX/IY stuff is slow on Z80 and nonexistent on LR35902, which lacks a couple of the Z80 prefixes. On LR35902, or on Z80 without (slow) IX/IY stuff, you need to make successively accessed properties either adjacent (so you can INC L to get to the next and/or use the (HL+) and (HL-) modes) or one bit apart in address (so you can SET/RES bits in L).
Oziphantom wrote:If X is your entity number, and Y is the field you want in the entity then ZP can hold an entity pointer table of which you can easily index into.
The typical way to do this on 6502 is X is your entity number, and a separate array of properties for each entity. This is the "structure of arrays" approach:

Code: Select all

.bss
actor_xhi: .res NUM_ACTORS
actor_x: .res NUM_ACTORS
actor_xlo: .res NUM_ACTORS
actor_dx: .res NUM_ACTORS
actor_dxlo: .res NUM_ACTORS
actor_y: .res NUM_ACTORS
actor_ylo: .res NUM_ACTORS
actor_dy: .res NUM_ACTORS
actor_dylo: .res NUM_ACTORS
actor_class: .res NUM_ACTORS  ; index into move vtable and sprite sheet pointers
actor_frame: .res NUM_ACTORS
actor_frame_sub: .res NUM_ACTORS
actor_health: .res NUM_ACTORS
actor_hurt_ht: .res NUM_ACTORS
User avatar
Myask
Posts: 965
Joined: Sat Jul 12, 2014 3:04 pm

Re: Why NES have too ugly arch?

Post by Myask »

za909 wrote:As far as instruction sets go, sometimes I look at the Z80 set with quite some envy. It may be slow as all hell but it sure has some clever instructions that I could really use in NES programs. For example the bit tests that can test a single bit in a register or in RAM. With the 6502 you can only check the top two bits with relative ease (and even the available addressing modes of BIT leave a lot to be desired) while the rest require you to load from the address and then AND to discard the bits you aren't interested in testing, or LSR to carry if you want to test bit 0.

I could say the same about the individual bit-setting and clearing instructions. No need to load from RAM and then use AND/ORA, and then storing the result. The Z80 even has those DMA-type instructions to work with a large chunk of data (moving it, finding a certain value in a table, etc.)
The DMA block-transfer instructions are also, unlike the NES's, beatable by fairly unremarkable programming.
Data tables for set/clear/toggle/test bit are also easy:($) 1 2 4 8 10 20 40 80; FE FD FB F7 EF DF BF 7F
Oziphantom
Posts: 1565
Joined: Tue Feb 07, 2017 2:03 am

Re: Why NES have too ugly arch?

Post by Oziphantom »

rainwarrior wrote:
Oziphantom wrote:Sure I could easily make my own standard but the standard set down by Chuck and Bill is the standard we all know and use. SEI Set Enable Interrupt - actually disables Interrupts. Not a 6502 design flaw, nothing wrong with the die, but still Chuck and Bill's misstep ;)
The early MOS datasheets for 6502 call them "Set Interrupt Disable Status" and "Clear Interrupt Disable Bit". I dunno where you got "Set Enable Interrupt" from, but if you were confused about the meaning of this mnemonic because of it, it wasn't from "Chuck and Bill".

(Edit: Okay apparently 3 other people said this already while I was typing.)
I stand correct, so it does... I guess I never really learnt the official names off by heart and over the last 28 years just derived them again Branch Not Equal, STore A etc. The irony is Set Enable Interupt and SEt Interupt both are still valid, in that it is what the instruction does. As you do Set Enable Interupt, just it's an active low signal. I feel it would be better to have been DII and ENI DIsable Interupts and ENable Interupts to be clearer. While I say its the biggest, it doesn't mean I think it is a big misstep.
rainwarrior wrote: A substitute wrapper for JSR (XXXX) takes only one extra line of code.

Code: Select all

	jsr jsr_xxxx
...
jsr_xxxx:
	jmp ($XXXX)
The -1 problem is self inflicted complexity. You can use RTS for a jump table implementation, but there's not a whole lot of need to do so. It's only really "necessary" in some very specific situations (e.g. you want the pointer on the stack).
(/)_. JSR (XXXX,x) is what I meant sorry, I forgot the ,x and didn't notice until I saw your code. You can store the address into a location and jump to it, personally I use the rts as its smaller. Seems most of the NES code I have read does the table after a JSR then looks up the stack to read beyond the caller method, fair statement?
User avatar
rainwarrior
Posts: 8732
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Why NES have too ugly arch?

Post by rainwarrior »

Oziphantom wrote:Seems most of the NES code I have read does the table after a JSR then looks up the stack to read beyond the caller method, fair statement?
I've seen that particular technique in one or two NES games, yes.
Oziphantom
Posts: 1565
Joined: Tue Feb 07, 2017 2:03 am

Re: Why NES have too ugly arch?

Post by Oziphantom »

tepples wrote: And then you formally prove that NMI-in-NMI can never happen, right?

Code: Select all

Save registers
Do the critical thing
Ack the interupt
Do something long and you don't care if it is interrupted like prepare animation data, shift memory etc 
restore registers
rti
Once you do the Acknowledge then your interrupt handler can fire again, and hence you can NMI in your NMI. If you hardware requires acknowledgement for NMIs. Yes in most cases you can guarantee to yourself that you will not NMI in your NMI. I would think on a NES if you got bogged down in doing something long that only happens every now and then, for example too many entities trying to update their animation data, that allowing the NMI to trigger again would be beneficial as you then don't drop a frame and get marked down for "slow-down" ;) Adding the necessary safe guards to stop the update animations function being called, so things at least move and maybe make sure the player updates.

tepples wrote:
tokumaru wrote:LDA (ZP, X) can be useful for accessing collections of streams, such as the different channels of a song.
As seen in, for example, the Pently music engine. It stores pointers to note attack data, sound effect data, and note pattern data for each of the four APU channels, plus one more pointer to note pattern data for the attack track. But it might not appear so useful if your only 6502 experience is on 6502-based home computers such as Apple IIe and Commodore 64, where BASIC uses up most of zero page.
Sure its not useless as in has absolutely no use what so ever. Just it is one the of the much lesser used instructions( but on a NES they could have re-purposed CLD and SED though right ;) ). The Audio Stream example is basically the use for it. Of which I would argue that doing the ,x,y double lookup is more useful and for the audio case you can do a ldy #0 and pay the 3 clock penitently to get just a ,x version again. Having said that, you probably want to read a value out of the audio stream to which loading Y with the current music data index and doing a LDA (MusicTable,Channel),DataIndex would be handy in that case anyway right?
Its other top use is I want to do LDA(ZP) but Y has something useful so I will LDX #0 and LDA(ZP,x) instead ;) Also good for burning 6 clocks in 2 bytes ;)
BASIC has 3 jobs in this world, one to do a LOAD, two to do a RUN, three do an SYS and then it can get its fat arse out of the memory map ;)
tepples wrote:
Oziphantom wrote:If X is your entity number, and Y is the field you want in the entity then ZP can hold an entity pointer table of which you can easily index into.
The typical way to do this on 6502 is X is your entity number, and a separate array of properties for each entity. This is the "structure of arrays" approach:

Code: Select all

.bss
actor_xhi: .res NUM_ACTORS
actor_x: .res NUM_ACTORS
actor_xlo: .res NUM_ACTORS
actor_dx: .res NUM_ACTORS
actor_dxlo: .res NUM_ACTORS
actor_y: .res NUM_ACTORS
actor_ylo: .res NUM_ACTORS
actor_dy: .res NUM_ACTORS
actor_dylo: .res NUM_ACTORS
actor_class: .res NUM_ACTORS  ; index into move vtable and sprite sheet pointers
actor_frame: .res NUM_ACTORS
actor_frame_sub: .res NUM_ACTORS
actor_health: .res NUM_ACTORS
actor_hurt_ht: .res NUM_ACTORS
Struct of Arrays is the way to do it sure, but sometimes you need a double look up, more for entity map data rather than live entity state, I would think. You want to compress the data used to position and set up entities, of which say a trigger has a very different data needs to moving enemy, or a spawner etc
Post Reply