Hello World

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

tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

FinalZero wrote:
In SEGMENTS, use type=bss instead of type=rw for any RAM segment. For segments of type bss, the linker won't try to (uselessly) put initial values for the segment into the ROM file.
Why is it useless though? Isn't data stored there? I don't see why it should be any different from the CODE segment.
Data is stored in RAM, but the only reason you'd ever want to also store it in ROM is if you plan to copy it to RAM at the start of the program, such as a small piece of code related to bank switching. Here are the traditional definitions of the segments:
  • CODE: stored in ROM, used in ROM
  • RODATA: stored in ROM, used in ROM. Primary difference from CODE is that some architectures such as 65816 and 8086 allow for a separate program bank and data bank.
  • DATA: stored in ROM, copied to RAM in init code (which isn't written automatically for you), used in RAM
  • BSS: stored nowhere, cleared to zero* in init code, used in RAM
  • ZEROPAGE: like BSS but in $0000-$00FF
However, running the debugger, it seems to be alternating between $0002 and $FFFF. I don't know why.
What values end up in $FFFA through $FFFF? There should be three addresses (in the typical reverse byte order of the 6502); do they point anywhere familiar?
Edit: I forgot to change the number of PRG banks in the header back to 1. Doing that, it runs code that I wrote. I think I made an error in the code though, because it isn't doing the addition that I wanted it to do.

Edit Edit: It seems to be interrupting every so often, and then returning to the wrong address. Ideas as to why?
Where do $FFFA-$FFFB and $FFFE-$FFFF point? Are you pushing or pulling anything in your NMI and IRQ handlers?


* Yes, tokumaru, ca65 was originally intended to support a C compiler, and the C language does specify clearing uninitialized variables in the init code. In pure assembly language programs, one can use an alternate convention that each is responsible for clearing its own memory.
User avatar
FinalZero
Posts: 152
Joined: Thu Dec 03, 2009 7:27 am
Contact:

Post by FinalZero »

Where do $FFFA-$FFFB and $FFFE-$FFFF point? Are you pushing or pulling anything in your NMI and IRQ handlers?
What values end up in $FFFA through $FFFF? There should be three addresses (in the typical reverse byte order of the 6502); do they point anywhere familiar?
The vectors point to an RTI, init code, and RTI, respectively. No, they only point to an NMI. At those locations are indeed the routines.

Code: Select all

.segment "VECTORS"
;-------------------------------------------------------------------------------

.addr nmi, reset, irq
;-------------------------------------------------------------------------------

.code
;-------------------------------------------------------------------------------

.proc nmi
	rti
.endproc
;-------------------------------------------------------------------------------

; Inits everything.
.proc reset
	; Clears the flags.
	clc
	cli
	clv
	
	; Sets the stack pointer.
	ldx #$FF
	txs
	
	jmp main
.endproc
;-------------------------------------------------------------------------------

.proc irq
	rti
.endproc
;-------------------------------------------------------------------------------
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

Having IRQ pointing at an RTI will generally work. But in the NMI handler, you'll usually want to set a variable to notify the main program that a vertical blank has begun. So that I don't have to push and pull A, I just have it do inc nmis.
User avatar
thefox
Posts: 3139
Joined: Mon Jan 03, 2005 10:36 am
Location: Tampere, Finland
Contact:

Post by thefox »

tepples wrote:Having IRQ pointing at an RTI will generally work.
Depends on the definition of "work". If the IRQ doesn't get acknowledged automatically somehow (most of them don't), it'll result in an infinite loop. In a debug build the best way to handle the IRQ might be to display a fatal error message. However, if an unexpected IRQ happens in an program, you've got worse problems on your hands...

All I'm saying is people should understand that the RTI in the IRQ routine doesn't really accomplish that much more than having the IRQ vector point to $0000 (for example).
User avatar
FinalZero
Posts: 152
Joined: Thu Dec 03, 2009 7:27 am
Contact:

Post by FinalZero »

Having IRQ pointing at an RTI will generally work. But in the NMI handler, you'll usually want to set a variable to notify the main program that a vertical blank has begun. So that I don't have to push and pull A, I just have it do inc nmis.
What and where is "nmis"? Does it simply count how many NMIs there's been? Is that all that needs fixed?
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

FinalZero wrote:What and where is "nmis"?
It's a variable anywhere in RAM.
Does it simply count how many NMIs there's been?
Yes. If you keep NMIs always enabled, you can wait for the next VBlank with code like this:

Code: Select all

	lda nmis
WaitVBlank:
	cmp nmis
	beq WaitVBlank
The loop basically waits for the variable to change. This works well in most cases, it's only bad if you have raster effects near the top of the screen (like status bars) and you can't guarantee that frame calculations will never spill into the next frame.
Is that all that needs fixed?
Can't tell without seeing what else your program does.
User avatar
FinalZero
Posts: 152
Joined: Thu Dec 03, 2009 7:27 am
Contact:

Post by FinalZero »

The loop basically waits for the variable to change. This works well in most cases, it's only bad if you have raster effects near the top of the screen (like status bars) and you can't guarantee that frame calculations will never spill into the next frame.
But where should this loop be located? in the reset code?
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

FinalZero wrote:But where should this loop be located?
Wherever a wait for VBlank is necessary. The typical structure of a game loop is: 1. update the game world using controller input and A.I.; 2. wait for VBlank; 3. update VRAM using data computed last frame; 4. update the audio; 5. go back to 1;

The wait for VBlank not only makes sure that the VRAM updates will take place during VBlank, but it also syncs the game frames to the refresh rate of the console, effectively making the game run at a steady pace.
in the reset code?
No, this has nothing to do with the reset.
User avatar
FinalZero
Posts: 152
Joined: Thu Dec 03, 2009 7:27 am
Contact:

Post by FinalZero »

Wherever a wait for VBlank is necessary. The typical structure of a game loop is: 1. update the game world using controller input and A.I.; 2. wait for VBlank; 3. update VRAM using data computed last frame; 4. update the audio; 5. go back to 1;
How do I know when "wherever" is (for the vblank)?
The wait for VBlank not only makes sure that the VRAM updates will take place during VBlank, but it also syncs the game frames to the refresh rate of the console, effectively making the game run at a steady pace.
So, this is my next step: Trying to display something on screen, preferably the numbers that I've successfully calculated.

I set the disable interrupt flag for the moment so I could test my math routines. They work! I successfully computed "14 9 7 + -". So, I want to ask, what kind of structuring do people usually use for procedures and macros? The couple routines I made so far are stack-based on the zero page. Is that feasible for a full program? What do NES games usually do? Does it depend whether it's a slow-paced RPG or a quick-paced action/adventure game?
CODE: stored in ROM, used in ROM
RODATA: stored in ROM, used in ROM. Primary difference from CODE is that some architectures such as 65816 and 8086 allow for a separate program bank and data bank.
DATA: stored in ROM, copied to RAM in init code (which isn't written automatically for you), used in RAM
BSS: stored nowhere, cleared to zero* in init code, used in RAM
ZEROPAGE: like BSS but in $0000-$00FF
I see now that my understanding of the bss segment was wrong, though it makes sense now; It's simpler to simply remember how many null bytes to reserve in RAM instead of actually storing all of them in ROM.

Also, the zeropage can't have initial values? It's limited like the bss segment?

* * *

A couple of other thoughts:

1) FCEUX's debugger does something it shouldn't. When scrolling up, it scrolls up by byte, instead of instruction. Bytes that are part of multi-byte instructions are misrepresented as their own instructions until one scrolls up far enough so the program realizes that it's part of another instruction.

2) How/where are palettes stored? They're not stored in .chr files, are they? Is there an editor for them (palettes)?

3) I had something else that I was going to say, but I can't remember now. =/

* * *

One last thing: I want to thank everybody who's been so patient and helpful in answering my questions. You've been indispensable so far.
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

FinalZero wrote:
Wherever a wait for VBlank is necessary. The typical structure of a game loop is: 1. update the game world using controller input and A.I.; 2. wait for VBlank; 3. update VRAM using data computed last frame; 4. update the audio; 5. go back to 1;
How do I know when "wherever" is (for the vblank)?
After the reset code finishes and the background is copied into the nametables, you start the game loop.
Also, the zeropage can't have initial values? It's limited like the bss segment?
Correct. If you want initial values there you'll have to copy them yourself.
1) FCEUX's debugger does something it shouldn't. When scrolling up, it scrolls up by byte, instead of instruction. Bytes that are part of multi-byte instructions are misrepresented as their own instructions until one scrolls up far enough so the program realizes that it's part of another instruction.
How would you predict how many bytes to scroll up? 6502 bytecode makes no self-synchronizing guarantee, unlike code in popular RISC architectures such as MIPS and ARM where every instruction is 16 or 32 bits long.
2) How/where are palettes stored? They're not stored in .chr files, are they? Is there an editor for them (palettes)?
A palette is just a list of 32 bytes, ordinarily stored in PRG ROM and copied to $3F00 during vertical blanking. Some NES-specific tile editors have NES-specific palette editors, but some other tile editors have palette editors more suitable for an RGB system (PC, SNES, GBA) than for a hue-lightness system like that of the NES.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

FinalZero wrote:How do I know when "wherever" is (for the vblank)?
There are many things you can't (or shouldn't) do outside of VBlank, such as turning rendering on and off, writing to VRAM, setting the scroll, and so on. Before doing those things, you should wait for VBlank. In the game loop, you typically wait for VBlank once and then perform all the tasks you couldn't before.
So, I want to ask, what kind of structuring do people usually use for procedures and macros? The couple routines I made so far are stack-based on the zero page. Is that feasible for a full program? What do NES games usually do? Does it depend whether it's a slow-paced RPG or a quick-paced action/adventure game?
I think you'll have to find your own style. Everyone's coding style is different, and although we could all tell you how we do things so that you can pick the way you prefer, I believe this would confuse you more than it would help! =)
When scrolling up, it scrolls up by byte, instead of instruction.
It has no way to know the exact instructions you used in the source code, since it can only see the resulting binary, which can be interpreted in many ways. Scrolling by byte allows you to align the disassembly correctly, which is better than if it tried to guess the instructions and guessed wrong. For example, the command LDA $03A9 assembles to $AD $A9 $03, and LDA #$03 assembles to $A9 $03. When scrolling up, should FCEUX scroll 2 bytes (and see LDA #$03) or 3 bytes (and see LDA $03A9)? It doesn't know, both are valid instructions.

You could ague that it could search for the longest instruction in order to avoid interpreting operands as opcodes, but even then there will be problems. Say that I have a table whose last byte is $AD, followed by the LDA #$03 instruction. The disassembler will think it's a LDA $03A9 instruction, which is not the case.

So trust me, it's better that it scrolls 1 byte at a time. Is it annoying that we see a bunch of garbage instructions before we see what we actually coded? Yes, but it would be far more annoying if the disassembler guessed wrong and we couldn't see what we actually coded at all.
How/where are palettes stored? They're not stored in .chr files, are they? Is there an editor for them (palettes)?
Depends on the programmer. Simple programs usually just have a list of 32 bytes representing all the colors, but more complex games might have smaller palettes that can be arranged differently.

Again, this is dependent on the coding style. The only important thing is that the colors are written to PPU address $3F00 during VBlank. Where the colors come from is completely dependent on game's architecture.
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

tokumaru wrote:
FinalBurn wrote:When scrolling up, it scrolls up by byte, instead of instruction.
It has no way to know the exact instructions you used in the source code, since it can only see the resulting binary
Can't a debugging emulator see the starting addresses of previously executed instructions (from the Code/Data Logger) and use those as alignment anchors?
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

I'm pretty sure that there are workarounds (by "workarounds" I mean imperfect solutions that will still break under certain conditions), but is it really worth the trouble?
User avatar
FinalZero
Posts: 152
Joined: Thu Dec 03, 2009 7:27 am
Contact:

Post by FinalZero »

After the reset code finishes and the background is copied into the nametables, you start the game loop.
Okay. Also, a question: So, if I permanently disable interrupts by setting the flag, I won't be able to update the screen, right?
How would you predict how many bytes to scroll up? 6502 bytecode makes no self-synchronizing guarantee, unlike code in popular RISC architectures such as MIPS and ARM where every instruction is 16 or 32 bits long.
It has no way to know the exact instructions you used in the source code, since it can only see the resulting binary, which can be interpreted in many ways. Scrolling by byte allows you to align the disassembly correctly, which is better than if it tried to guess the instructions and guessed wrong. For example, the command LDA $03A9 assembles to $AD $A9 $03, and LDA #$03 assembles to $A9 $03. When scrolling up, should FCEUX scroll 2 bytes (and see LDA #$03) or 3 bytes (and see LDA $03A9)? It doesn't know, both are valid instructions.

You could ague that it could search for the longest instruction in order to avoid interpreting operands as opcodes, but even then there will be problems. Say that I have a table whose last byte is $AD, followed by the LDA #$03 instruction. The disassembler will think it's a LDA $03A9 instruction, which is not the case.

So trust me, it's better that it scrolls 1 byte at a time. Is it annoying that we see a bunch of garbage instructions before we see what we actually coded? Yes, but it would be far more annoying if the disassembler guessed wrong and we couldn't see what we actually coded at all.
Ack, I suppose that you're right. There's no way for the program to know whether something is code or data.
I think you'll have to find your own style. Everyone's coding style is different, and although we could all tell you how we do things so that you can pick the way you prefer, I believe this would confuse you more than it would help! =)
I'm not sure that a description of the structure would confuse me. I'm quite a bit more experienced than some kiddie programmer.
A palette is just a list of 32 bytes, ordinarily stored in PRG ROM and copied to $3F00 during vertical blanking. Some NES-specific tile editors have NES-specific palette editors, but some other tile editors have palette editors more suitable for an RGB system (PC, SNES, GBA) than for a hue-lightness system like that of the NES.
What tile editor do you recommend? Are they any good NES-specific ones? I've used TileMolester and yychr, but each lacks capabilities that the other has, so neither is The One Tile Editor to Rule Them All.

I really don't want to write my own, yet the limited choices and capabilities is annoying...
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

FinalZero wrote:
After the reset code finishes and the background is copied into the nametables, you start the game loop.
Okay. Also, a question: So, if I permanently disable interrupts by setting the flag, I won't be able to update the screen, right?
Not exactly. The vertical blank interrupt is an NMI, and NMI comes through whether the interrupt priority level is 0 or 1. SEI blocks only IRQ. You have to block NMI at the source, and bit 7 of PPUCTRL ($2000) controls the source. As long as bit 7 of PPUCTRL is turned on (LDA #$80 STA PPUCTRL), the following will work:

Code: Select all

; $FFFA points here
.proc nmi
  inc nmis
  rti
.endproc

; your game loop calls this just before shoving stuff into VRAM
.proc wait4vbl
  lda nmis
loop:
  cmp nmis
  beq loop
  rts
.endproc
This .proc might confuse you. But it's just a way to hide the labels defined inside the .proc from view outside the .proc, so that multiple subroutines can share the same names for internal labels. For example, the symbol inside wait4vbl ends up called wait4vbl::loop, and other subroutines won't be defining symbols that start with "wait4vbl::".
I'm quite a bit more experienced than some kiddie programmer.
In what other language or for what other platform have you made a video game? Perhaps I could help explain things with analogies to that platform.
What tile editor do you recommend? Are they any good NES-specific ones? I've used TileMolester and yychr, but each lacks capabilities that the other has, so neither is The One Tile Editor to Rule Them All.
I just use GIMP to make my tile sheets and then run a Python program to convert .png to .chr.
Post Reply