Page 1 of 18

Hello World

Posted: Thu Dec 23, 2010 3:19 pm
by FinalZero
I'm new to programming on the 6502. I've only been able to do simple things like move a character (as a sprite (I think))) around the screen with the d-pad made in nbasic (which compiles to nesasm assembly). Reading threads, it looks like nesasm is one of the stranger assemblers out there, so I'd like to try a more conventional one, like ca65. The problem is, I have no idea how to use it. The reason I started with nbasic and nesasm is that it was the only thing I could find simple, commented example programs for. And so, I humbly ask, how does one create a simple program that prints out "Hello World!"?

Also, being somewhat relevant, I have plenty of experience programming in higher level languages like C++ and Java, but have only recently learned some x86 assembly, and now, 6502 assembly.

Posted: Thu Dec 23, 2010 4:02 pm
by 3gengames
You should learn most of the 6502 assembly, as that is the syntax all the assemblers use standardly, the only real features others add are tags and stuff. Look up in the assemblers files for the README or something along those lines to find what it offers and how to access those features like tags and such.

Posted: Thu Dec 23, 2010 5:52 pm
by Dwedit
Here's my copy of "Hello world"

It demonstrates how to wait for the NES to warm up, clear the RAM, clear the nametables, load in a font, set the palette, wait for vblank, and turn the screen back on.
But it's not very well commented.

Posted: Thu Dec 23, 2010 9:18 pm
by koitsu
Dwedit wrote:Here's my copy of "Hello world"

It demonstrates how to wait for the NES to warm up ...
Can you explain what this means and refers to, both on the hardware as well as in the assembly code? I'd like to know what "warm up" period is required, how long it is, and why it's needed, for the NES to become "usable". I'm not talking about register or RAM initialisation -- I'm talking about what it means to "wait for the NES to warm up".

Thanks!

EDIT: Is this what "warm up" means? If so, okay got it. If not, an explanation would be cool.

Posted: Thu Dec 23, 2010 11:14 pm
by 3gengames
I think by warm up, he means the 2 frames needed to go by before the APU is ready to output sound.

Posted: Fri Dec 24, 2010 12:03 am
by tokumaru
koitsu wrote:I'd like to know what "warm up" period is required, how long it is, and why it's needed, for the NES to become "usable".
It seems that some parts of the NES do not behave consistently if used as soon as the system is powered up. A lot of PPU writes are ignored, for example (this has been verified by blargg, I think).

Because of that, it's good practice to wait at least 2 whole frames before interacting with the PPU or APU. The waiting can be done by pooling the VBlank flag, which appears to behave consistently during the warm up period.
FinalZero wrote:Reading threads, it looks like nesasm is one of the stranger assemblers out there
Yes, NESASM doesn't have the reputation of being the most reliable assembler out there, but, unfortunately, NBASIC isn't that hot either. I seem to remember people here saying that programs made with it had compatibility issues with actual consoles, meaning its internal functions weren't properly coded. I think everyone will agree that a NES program that doesn't run properly on the NES is a huge FAIL.

The best way to make NES programs is obviously straight 6502 assembly, so if you think you can handle it that's definitely the best thing you can do. I'm not aware of any magical programming language that makes it easy to code NES programs that isn't either severely limited or just plain buggy.

Posted: Fri Dec 24, 2010 5:47 am
by tepples
tokumaru wrote:It seems that some parts of the NES do not behave consistently if used as soon as the system is powered up. A lot of PPU writes are ignored, for example (this has been verified by blargg, I think).

Because of that, it's good practice to wait at least 2 whole frames before interacting with the PPU or APU. The waiting can be done by pooling the VBlank flag, which appears to behave consistently during the warm up period.
Two frames for the APU? The only mention of an APU delay is a 2048 cycle delay in Brad Taylor's APU doc, which I conjectured to be waiting for the period dividers to finish their first cycle. My programs have always set up the APU while waiting for the PPU, especially because two IRQ sources are from the APU.
I'm not aware of any magical programming language that makes it easy to code NES programs that isn't either severely limited or just plain buggy.
But sometimes severely limited is exactly what's needed. Consider WarioWare DIY: out of all the power of the DS, it limits the developer to 15 sprites of variable size, no sprite flipping, no background scrolling unless faked with sprites, no palette swapping or other sharing of animation between sprites, no state other than which of four animations it's in and one general-purpose flag, input as limited as the Zapper (tapping objects on the screen or tapping the background), and making a game longer than eight seconds requires save hacking.

Posted: Fri Dec 24, 2010 9:39 am
by tokumaru
tepples wrote:My programs have always set up the APU while waiting for the PPU, especially because two IRQ sources are from the APU.
I see little advantage in doing anything during this waiting period (before you mention clearing RAM, I'll remind you that I'm against this), so aside from disabling IRQs I don't really do anything.

Posted: Sun Dec 26, 2010 9:18 pm
by FinalZero
Thank you for all the replies.
You should learn most of the 6502 assembly, as that is the syntax all the assemblers use standardly, the only real features others add are tags and stuff. Look up in the assemblers files for the README or something along those lines to find what it offers and how to access those features like tags and such.
Will do.
Dwedit wrote:Here's my copy of "Hello world"

It demonstrates how to wait for the NES to warm up, clear the RAM, clear the nametables, load in a font, set the palette, wait for vblank, and turn the screen back on.
But it's not very well commented.
Trying to assemble it with ca65 produces a bunch of ".db is not a recognized control command" errors.

I see you made the font by just inserting it into the file with a bunch of .db's. Is there a way to include it from a separate file though?

Code: Select all

load_font:
	lda #font&255
	sta addy
	lda #font/256
	sta addy+1
	ldx #3
	ldy #0
What do the first two lda's do? I don't understand the #font& and #font/ parts. Does a preceding # mean? Can that character be left out?

How would one print something out if their font table didn't match the ASCII ordering?

Code: Select all

main_loop:
	jsr waitframe
	
	lda #0
	sta $2005
	sta $2005
	lda #$18
	sta $2001
	
	lda #$80
	sta $2000

	jmp main_loop
Why is "sta $2005" repeated twice? Isn't that redundant?

This seems to be good documentation: http://www.obelisk.demon.co.uk/6502/instructions.html
I see there's adc and sbc, but no add and sub. What does one do if they wish to add or subtract without the carry? Just clear the carry flag first? Also, it's strange that there's asl and lsr, but no asr and lsl.

Iirc, the NES's processor did away with Binary Coded Decimal capabilities, so what happened the to associated register flag? Does one get errors if it's ever set?

Posted: Sun Dec 26, 2010 11:02 pm
by tepples
FinalZero wrote:
Dwedit wrote:Here's my copy of "Hello world"

It demonstrates how to wait for the NES to warm up, clear the RAM, clear the nametables, load in a font, set the palette, wait for vblank, and turn the screen back on.
But it's not very well commented.
Trying to assemble it with ca65 produces a bunch of ".db is not a recognized control command" errors.
ca65 uses .byt instead.
I see you made the font by just inserting it into the file with a bunch of .db's. Is there a way to include it from a separate file though?
.include "somesourcefile.s"
.incbin "somebinaryfile.chr"

Code: Select all

load_font:
	lda #font&255
	sta addy
	lda #font/256
	sta addy+1
	ldx #3
	ldy #0
What do the first two lda's do? I don't understand the #font& and #font/ parts. Does a preceding # mean? Can that character be left out?
# means the following value is a number to put directly into A, not an address from which to load the value into A. Look at the "immediate" addressing mode.
How would one print something out if their font table didn't match the ASCII ordering?
There are two ways. Super Mario Bros. does it by specifying the character encoding within the assembler. (In ca65 use .charmap commands.) Mega Man 5 does it by translating ASCII characters through a lookup table to get glyph tile indices.
Why is "sta $2005" repeated twice? Isn't that redundant?
PPUSCROLL ($2005) is a port on the PPU. Alternating writes set horizontal and vertical background scroll coordinates. You need two writes so that the first one sets the horizontal and the second one sets the vertical.
This seems to be good documentation: http://www.obelisk.demon.co.uk/6502/instructions.html
I see there's adc and sbc, but no add and sub. What does one do if they wish to add or subtract without the carry? Just clear the carry flag first?
Yes. A lot of programmers define ADD and SUB macros that clear carry before adding or set carry before subtracting.
Also, it's strange that there's asl and lsr, but no asr and lsl.
LSL is redundant, as both arithmetic and logical shift put a 0 in the one's place. There is ASR, just not as one instruction: do a CMP #$80 followed by ROR.
Iirc, the NES's processor did away with Binary Coded Decimal capabilities, so what happened the to associated register flag? Does one get errors if it's ever set?
The decimal flag exists, but the adder just ignores it. CLD and SED are as good as NOP.

Posted: Mon Dec 27, 2010 12:36 am
by FinalZero
ca65 uses .byt instead.
or .byte, apparently. So, I changed those and another to .word, and... it assembled! But it doesn't run on FCEU. =/
.include "somesourcefile.s"
.incbin "somebinaryfile.chr"
Simple enough.
# means the following value is a number to put directly into A, not an address from which to load the value into A. Look at the "immediate" addressing mode.
Ah, I see.
There are two ways. Super Mario Bros. does it by specifying the character encoding within the assembler. (In ca65 use .charmap commands.) Mega Man 5 does it by translating ASCII characters through a lookup table to get glyph tile indices.
That .charmap command looks very attractive. Does the second method described translate everything at assembly time, or at run time? Also, do the .charmap commands take effect globally?, or only after? Looking at the Hello World example given:

Code: Select all

.byte "NES",$1A,$01,$00,$20,$00
The .charmap command would change the "NES" away from what was intended, no?
PPUSCROLL ($2005) is a port on the PPU. Alternating writes set horizontal and vertical background scroll coordinates. You need two writes so that the first one sets the horizontal and the second one sets the vertical.
Is there documentation that describes all those special things at addresses like that? Looking at that example file given, I see...

Code: Select all

OAM       = $0200

PPUCTRL   = $2000
PPUMASK   = $2001
PPUSTATUS = $2002
PPUSTAT = $2002
SPRADDR   = $2003  ; always write 0 here and use DMA from OAM
PPUSCROLL = $2005
PPUADDR   = $2006
PPUDATA   = $2007

SPRDMA    = $4014
SNDCHN    = $4015
JOY1      = $4016
JOY2      = $4017
Yes. A lot of programmers define ADD and SUB macros that clear carry before adding or set carry before subtracting.
Alright.
LSL is redundant, as both arithmetic and logical shift put a 0 in the one's place.
A strange property; x86 does the same. Perhaps they did it because putting a 1 in the one's place would be rather useless, despite making the set of commands regular.
There is ASR, just not as one instruction: do a CMP #$80 followed by ROR.
Ok.
The decimal flag exists, but the adder just ignores it. CLD and SED are as good as NOP.
Ok.

-----

By the way, is there a way of doing multiline comments like /* */ in C? Or must one prefix every line with a semicolon?

Also, while we're on the topic of character maps, how would something like Dual Tile Encoding be implemented? Also, are there any Japanese games that implement dakuten and handakuten for their characters by having a dakuten/handakuten tile and simply overlapping it with another, as opposed to creating a whole new set of tiles with the dakuten/handakuten added?

Posted: Mon Dec 27, 2010 2:03 am
by koitsu
FinalZero wrote:

Code: Select all

load_font:
	lda #font&255
	sta addy
	lda #font/256
	sta addy+1
	ldx #3
	ldy #0
What do the first two lda's do? I don't understand the #font& and #font/ parts. Does a preceding # mean? Can that character be left out?
As tepples described, the "#" character means immediate addressing -- think "literal value". More on that in a sec.

The &255 and /256 syntax appears to be a bunch of nonsense for getting the low and high bytes of the 16-bit address of a label (in this case, the low and high bytes, respectively, of font). I call it nonsense because there's some history indicating only devel builds of ca65 support the .LOBYTE and "<" directives, as well as .HIBYTE and ">" directives. God I hate that assembler. I seriously don't understand why people bother with it.

Here's a more much more common syntax you'll see, and makes a lot more sense IMHO:

Code: Select all

	lda #<font
	sta addy
	lda #>font
	sta addy+1
In English: if font is located at location $894F, then the first LDA will load the value $4F into the accumulator, while the second LDA would load $89. If you removed use of immediate addressing, you'd have:

Code: Select all

	lda <font
	sta addy
	lda >font
	sta addy+1
In English: if font is located at $894F, then the first LDA will load the value stored in memory at location $4F into the accumulator, while the second LDA would do the same but for from location $89.
FinalZero wrote:By the way, is there a way of doing multiline comments like /* */ in C? Or must one prefix every line with a semicolon?
There's no "universal standard" for this. It's important to understand that assembler syntaxes vary greatly depending on the assembler, and it's your responsibility to learn/read up on what your assembler-of-choice supports. You should never assume that an assembler has something even remotely "C-like" in it.

Generally speaking, most assemblers assume that anything past (and including) a semicolon are comments. Example:

Code: Select all

     some_code       ; in-line comment
     some_code       ; hello world
     some_code       ; i like rice

;
; Below code rides snakes around Arabia, or Persia,
; or China, or tepple's living room.
;
     some_code
     some_code
     ...
Some assemblers support things like the .COMMENT and .ENDCOMMENT pseudo-directives, which would act more like a /* */ block-style comment in C. E.g.:

Code: Select all

.COMMENT
Below code rides snakes around Arabia, or Persia,
or China, or tepple's living room.
.ENDCOMMENT

     some_code
     some_code
     ...
But again, there's no "standard" -- you need to read the documentation associated with your assembler to find out. If there is no documentation, don't bother using that assembler.

Hope this helps. Cheers!

Posted: Mon Dec 27, 2010 2:36 am
by FinalZero
God I hate that assembler. I seriously don't understand why people bother with it.
What assembler do you use?
There's no "universal standard" for this. It's important to understand that assembler syntaxes vary greatly depending on the assembler, and it's your responsibility to learn/read up on what your assembler-of-choice supports. You should never assume that an assembler has something even remotely "C-like" in it.
Digging through ca65's documentation there's a ".feature c_comments" option, thus allowing multiline comments.

Posted: Mon Dec 27, 2010 2:41 am
by koitsu
FinalZero wrote:
God I hate that assembler. I seriously don't understand why people bother with it.
What assembler do you use?
There's already a relevant thread pertaining to assembler debates and choices. I personally tend to stick to asm6 and x816, as their syntax and overall style mimic what I'm used to (ORCA/M and Merlin 16+).

Posted: Mon Dec 27, 2010 3:24 am
by FinalZero
koitsu wrote:
FinalZero wrote:
God I hate that assembler. I seriously don't understand why people bother with it.
What assembler do you use?
There's already a relevant thread pertaining to assembler debates and choices. I personally tend to stick to asm6 and x816, as their syntax and overall style mimic what I'm used to (ORCA/M and Merlin 16+).
Ah, ok. I suppose the reason I'm trying out ca65 at the moment is because it has current development, which is likely to fix any bugs found (hopefully), and has lots of documentation.
Here's a more much more common syntax you'll see, and makes a lot more sense IMHO: [...]
I understand. Thank you.