To start programming as quickly as possible, we’ll walk through a simple C program that displays text on the NES screen.

Most programming tutorials include a simple “Hello, World!” example, which prints a single line of text to the console. Usually this only takes a few lines of code, but on the NES, it’s a little more complicated.

View Code
#include "neslib.h"

void main(void) {

  // set palette colors
  pal_col(0,0x02);	// set screen to dark blue

  // enable PPU rendering (turn on screen)
  ppu_on_all();

  // infinite loop
  while (1) ;
}

First, we include the NESLib header file. NESLib is a C library that gives us NES programming functions:

    #include "neslib.h"

We also need to include a pattern table, which contains the bitmap graphics used by the game. The IDE includes a generic pattern table with letters and numbers. We’ll include it with a special #link command:

    //#link "chr_generic.s"

When our game starts up, it runs the main() function. This, in turn, calls other functions which initialize the system and start our game.

    void main(void) {
        ...
    }

The first thing our main function will do is set the color of the entire screen. This is done with the pal_col() function:

      pal_col(0,0x02);  // set screen to dark blue

We set it to hexadecimal 0x02, which translates into dark blue.

NES graphics are made out of 8x8 tiles. Each tile can have three different colors, chosen from a palette of 64 colors. Let’s set the palette colors now:

      pal_col(1,0x14);  // pink
      pal_col(2,0x20);  // grey
      pal_col(3,0x30);  // white

These tiles are arranged in a 32 column by 30 row grid called a nametable. The nametable chooses characters from the pattern table that go into a scrollable background layer.

To display our message, we need to put the bytes that comprise the text string HELLO, WORLD! into the nametable.

Writing to video memory requires two steps. First, we set the address to be written with the vram_adr() function.

      vram_adr(NTADR_A(2,2));

NTADR_A@ is a C \term{macro} which calculates the \term{address} of a given column and row in a nametable. In our example, we've placed our @HELLO, WORLD! on column two, line two.

Next, we call the vram_write() function, passing our HELLO, WORLD! string along with the number of bytes to write into video memory — 13 characters, in this case.

      vram_write("HELLO, WORLD!", 13);

When our program starts, the screen is turned off. To display our text, we have to turn on the Picture Processing Unit (or PPU):

      ppu_on_all();

We don’t have anything else to do in this demo, so we enter an infinite loop:

      while (1);

This keeps the CPU from exiting the main() function, which isn’t allowed by our NES library — games are supposed to run forever, or at least until the console is reset.

The CPU will remain busy inside the infinite loop, but the PPU will continue to run, outputting frames of video to the emulated CRT. You should see the following message on the emulator screen:

View Code
#include "neslib.h"

//#link "chr_generic.s"

void main(void) {

  // set palette colors
  pal_col(0,0x02);	// set screen to dark blue
  pal_col(1,0x14);	// fuchsia
  pal_col(2,0x20);	// grey
  pal_col(3,0x30);	// white

  // write text to name table
  vram_adr(NTADR_A(2,2));		// set address
  vram_write("HELLO, WORLD!", 13);	// write bytes to video RAM

  // enable PPU rendering (turn on screen)
  ppu_on_all();

  // infinite loop
  while (1) ;
}

Tweaking the Code

Now let’s make a few changes to the example. Suppose we want the Hello, World! message to show up for just 10 seconds and then disappear.

To do this, we’ll have to count video frames in a loop. We’ll declare a local variable called x to hold the loop counter value.

Our C compiler requires us to declare all local variables at the beginning of a function, so we start by declaring x:

  void main(void) {
    int x;                  // <-- add this line

We’ll use a for loop to count from 0 to 499. Each loop iteration, we call ppu_wait_frame(). This function keeps the CPU busy until the next frame starts, which happens 50 times a second (more on this in Chapter [chap:scrolling]{reference-type=“ref” reference=“chap:scrolling”}).

  for (x=0; x<500; x++) {   // <-- add these lines
    ppu_wait_frame();       // <-- after
  }             // <-- ppu_on_all()

After the loop ends, we call ppu_off() to turn the screen off:

  ppu_off();            // <-- add this line

We’ll review this C example in more detail in Chapter [chap:helloc]{reference-type=“ref” reference=“chap:helloc”}. In the meantime, let’s move onto reviewing some essential prerequisites for NES programming, starting with binary numbers.

To revert your changes back to the original code, select **File** from
the menu, choose **Revert to Original\...** and click **OK** when
prompted.
If you're already familiar with binary and hexadecimal numbers, logical
and shift operations, and unsigned vs. signed arithmetic, you can safely
skip to the next chapter.

Atari 2600 Stuff

View Code
	.include "vcs-ca65.h"

.segment "VECTORS"
VecNMI:    .word Reset
VecReset:  .word Reset
VecBRK:    .word Reset

.code
Reset:
	CLEAN_START
   nop
   jmp Reset

The INCLUDES option

View Code
---delete nop
---after CLEAN_START
 lda #$30
 sta COLUBK

An Example ASM Program

First we’re going to initialize the cart.

10a = 2
11print('my 1st line')
12print(f'my {a}nd line')
View Code
	include "cartheader.dasm"

Start
	sei       ; turn off interrupts
	ldy #5
	sty $d020 ; reset border color
Wait1
	jmp Wait1 ; endless loop

Why do we highlight??

.. code-block:: ca65

Foo:  lda #1
		rts
View Code
	include "cartheader.dasm"

; variables
Temp equ     $03
; program start
Start
	sei		; turn off interrupts
	ldy #0
	sty $d020	; reset border color
Loop
	lda Message,y	; load message byte
	beq EOM		; 0 = end of string
	clc
	adc #$c0	; + 192
	sta $400+41,y	; store to screen
	iny
	bne Loop	; next character
EOM
Wait1
	lda $d011
	bmi Wait1	; wait for line < 256
Wait2
	lda $d012	; get current scanline
Wait3
	cmp $d012
	beq Wait3	; wait for scanline to change
	lsr
	lsr
	clc
	adc Temp
	sta $d020	; set border color
	lda $d011	; get status bits
	bpl Wait2	; repeat until line >= 256
	sty $d020	; reset border color
	dec Temp	; scroll colors
	jmp Wait1	; endless loop
Message
; PETSCII - http://sta.c64.org/cbm64pet.html
	byte "HELLO`WORLDa"
	byte 0

Cool, eh??

Comments