The program below is written for xa65, a 6502/R65C02/65816 cross-assembler. It should be portable to other 6502 assemblers with minor tweaks. (The xa65 website is down as of the time of writing, but the program can be found in many places on the web, or from your friendly neighborhood Linux distro's package system.)

Some Notes

The $200X addresses used below are magic registers which control the NES's video memory. See NES PPU for details on their function, and for some brief info on how images are stored in the NES. (It's complicated.)

This program eschews the normal method of storing graphics in ROM (called CHR-ROMs) in favor of copying the graphics from PRG-ROM into CHR-RAM. There is not really a good reason for this, except you have to deal with fewer bank files.

Resources

hello.65

; load this at $8000
* = $8000

; Graphics data.
; Each line is a character:
; space H E L O , W R D !
chrdata:
.byte 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
.byte 0,102,102,126,102,102,102,0,0,0,0,0,0,0,0,0
.byte 0,126,96,120,96,96,126,0,0,0,0,0,0,0,0,0
.byte 0,96,96,96,96,96,126,0,0,0,0,0,0,0,0,0
.byte 0,60,102,102,102,102,60,0,0,0,0,0,0,0,0,0
.byte 0,0,0,0,0,24,24,48,0,0,0,0,0,0,0,0
.byte 0,102,102,102,102,126,102,0,0,0,0,0,0,0,0,0
.byte 0,124,102,124,120,108,102,0,0,0,0,0,0,0,0,0
.byte 0,124,102,102,102,102,124,0,0,0,0,0,0,0,0,0
.byte 0,24,24,24,24,0,24,0,0,0,0,0,0,0,0,0
.byte $ff

; The message "Hello, World!",
; chosen from the characters above.
message:
.byte 1,2,3,3,4,5,0,6,4,7,3,8,9,$ff

; Colors
palette:
.byte 13,32,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0


; the beginning
int_reset:
	; load graphics data into CHR-RAM
	lda #0
	sta $2006
	sta $2006
	ldx #0
ldc:	lda chrdata,x
	cmp #$ff
	beq cdone
	sta $2007
	inx
	jmp ldc
cdone:
	
	; write the message to the name table
	lda #$20
	sta $2006
	sta $2006
	ldx #0
ldm:	lda message,x
	cmp #$ff
	beq mdone
	sta $2007
	inx
	jmp ldm
mdone:
	
	; load the palette
	lda #$3f
	sta $2006
	lda #$00
	sta $2006
	ldx #0
ldp:	lda palette,x
	sta $2007
	inx
	cpx #$10
	bne ldp
	
	; reset PPU pointer
	; (otherwise everything is offset)
	lda #0
	sta $2006
	sta $2006
	
	; enable PPU
	lda #%10001000
	sta $2000
	lda #%00011110
	sta $2001
	
	; done, just wait around
main:	jmp main

; these interrupts aren't needed for this program
int_vblank:
int_irqbrk:
	rti

; fill the rest of this bank with zeroes
.dsb $bffa-*, 0

; except for the interrupt vectors
.word int_vblank, int_reset, int_irqbrk

hello.nes

To get the above code to work in an emulator, just assembling it isn't quite enough. You need to wrap it with a header that gives the emulator a little more information about your program. This is beyond the scope of this writeup.

Here is the assembled ROM, gzipped and base64 encoded. Save this into hello.b64 and run base64 -d hello.b64 | gunzip > hello.nes, then use your favorite NES emulator.

H4sIAF2cPk4AA+3QIQ7CMBTG8Y4NBoIE2UBCegQOQAIGt2C4wFQVB0AsS+cwOwS4Kk5A0DiOAA6J
QY+OBLYZFPL/S833Xt9r0uViNfTED1qnWusqp/EmjtMqx6VanrrbetrcIeWktq/cWPUSnWzWjewk
9VkphfzGwmv5ftAWnSD0u72ir5ovWZF3lDt7cRTmXDzCPFT36Gys+tZ3Vf1m7MwV30OuczBl9Tq4
PD9r7DYXyo5zT0Uym//6JgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA/mOUncwoewEUtjtMEEAAAA==

Pseudo_Intellectual says re NES Hello World (how-to): this could be done much more easily using the NES' Family BASIC 8)

raincomplex says haha, indeed. although, going that route, you may as well use the C64

Pseudo_Intellectual says Fair enough, though the C64 won't come pre-loaded with Donkey Kong sprites to dance while you greet the world 8)

Log in or registerto write something here or to contact authors.