News: 11 March 2016 - Forum Rules

Author Topic: ELI5: Nerdy Nights Week 3  (Read 3876 times)


  • RHDN Patreon Supporter!
  • Hero Member
  • *****
  • Posts: 939
    • View Profile
ELI5: Nerdy Nights Week 3
« on: June 28, 2017, 10:36:47 am »
As you've no doubt guessed, I'm following the Nerdy Nights NES programming tutorial,
additionally I am not the sharpest tool rusting in the yard. :P

What's going on is that Week 3 gives me a whole bunch of relatively easy chunks to write down but nothing that will compile into usable form, and then gives me all the answers all packed up and ready to go without really explaining a bunch of stuff, like I have no idea how IRQs work, or what clrmem is doing, why certain addresses need to be written to, what the stack is, why it tells me to use PPUMASK and then doesn't itself, or what some of these opcodes like BIT and BPL are doing, can someone explain some of these in a simple way or direct me to somewhere I can learn?

I mean I did eventually get it to compile into a colorful seizure loop, but that doesn't mean I really understand what I'm doing.


  • Sr. Member
  • ****
  • Posts: 498
    • View Profile
Re: ELI5: Nerdy Nights Week 3
« Reply #1 on: June 29, 2017, 12:29:52 am »
So the CPU isn't the end-all, be-all of a system. It's electrically wired to many components. As such, it has physical input/output wires to communicate with those components. IRQs (interrupt requests) are a way for certain hardware components to interrupt the CPU and tell it to handle something. As it actively interrupts code execution, it is only used for very high priority events (ie. vblank when the PPU can be written to). Lower priority events such as gamepad button presses are handled through hardware registers and the CPU must poll the register (read it periodically to check its state). Polling isn't appropriate for checking high priority events as it consumes CPU cycles to repeatedly check. Cycles more appropriately used for actual work.

So onto hardware registers. As I said, the CPU isn't a standalone unit. It can read/write memory addresses, but the NES CPU (A 2A03 containing a 6502 processor) translates certain addresses to certain input/output hardware pins which are connected to external components. You'll see this manifested in the NES memory map. The memory map shows the CPU's perspective of its environments. To learn more, you'd need to look at hardware schematics.

Stacks are a basic data structure. Think of a stack of trays where you can place (push) or remove (pop) trays. The NES stack is basically the program reserving memory. It's primarily used for very temporary storage. Preserving register and instruction address information across subroutine calls is one major example.

The program does use the PPUMASK. Check the STA $2001 line in the source code. I'm not sure if the intensity bits are used often, but it's a way to expand upon the NES's limited color palette. It's just an example after all.

For BIT and BPL, you should consult a 6502 opcode list.


  • Hero Member
  • *****
  • Posts: 2814
  • NES Junkie
    • View Profile
Re: ELI5: Nerdy Nights Week 3
« Reply #2 on: June 29, 2017, 01:26:52 am »
Klarth's reply is good, and I don't mean to step on his toes here, but I felt I could elaborate on a few things a bit:


I wouldn't worry about these until you get a very basic "hello world" program running and understanding how that works.

NMIs and IRQs are "interrupts".  As Klarth mentioned, these are external signals that are used to "cut into" or "interrupt" the running program to let you know that a certain event has happened.

NMIs and IRQs are mostly the same, but with some minor differences:

- The 'I' processor flag (see CLI/SEI instructions) will prevent IRQs from occurring, but will not stop NMIs.
- IRQs go the the IRQ vector ($FFFE) when they happen, whereas NMIs go to the NMI vector ($FFFA)
- On the NES, NMIs are used exclusively for notifying you when VBlank happens, whereas IRQs have various mapper-specific uses (though virtually all of them are raster related, like letting you know when a specific scanline is starting)

You can think of it this way -- the CPU is always running instructions.  When an interrupt happens, the CPU will stop running code from it's current position, and will jump to a new position (designated by the address in the NMI/IRQ vector).  Once you do an RTI, the interrupt code will stop, and the CPU will jump back to the code it was running before the interrupt happened.

Basically interrupts cause the system to jump to new code when something significant happens.  But to really understand why that's useful, you need to understand the basics of timing.  Which is not a topic I would recommend until you've got the basics of displaying a simple image on screen.


clrmem is clearing memory (RAM).

On the NES, RAM resides between the $0000-07FF address range.  On powerup, this RAM contains unreliable garbage, so it's good practice to clear it (set it all to zero) before using any of it.  clrmem accomplishes this with a basic loop:

Code: [Select]
; It's not mentioned, but for this loop to work properly, X must be 0 at the start
;   It is 0 in the code (due to the INX on line 19) but it's not exactly clear...

  LDA #$00          ; sets A=0
  STA $0000, x      ; writes A (0) to address $0000+X
  STA $0100, x      ; then to $0100+X
  STA $0200, x      ; then to $0200+X
  STA $0400, x      ; etc...
  STA $0500, x
  STA $0600, x
  STA $0700, x
  LDA #$FE
  STA $0300, x      ; then writes $FE to address $0300+X
  INX               ; Increments X
  BNE clrmem        ; Jumps back until X is zero, which won't happen until it wraps from $FF->$00

The loop works like so:  Each time the code runs (each "iteration" of the loop), 'X' will be a new value (one higher than it was before).  So "STA $0000,X" will write to address $0000 the first iteration, but will write to address $0001 the next time (because X=1), then $0002 the time after that (because X=2), and so on.

The last iteration, X will be $FF... which will write to $00FF.  Then, once it hits that INX, since X is only 8-bits wide, it will wrap from $FF to $00, resulting in the Z flag being set, and the BNE to not branch, allowing the code to exit the loop.

The end result is that all addresses in the $0000-07FF range get zero written to them (with the exception of the $0300-03FF page, which gets $FE written to it instead).

If might help to run this code in FCEUX and step through it line by line with the hex editor open so you can see how memory changes with each STA.

Why certain addresses need to be written to

This is a pretty generic question, as you don't specify which addresses you're talking about.

Klarth linked to the memory map, which is a good general outline.  But if you want more details about what certain addresses do, you can look at the nesdev wiki.  In fact, you might want to see if you can find answers to any questions on the nesdev wiki.

PPU (graphics) registers:

APU (sound) registers (I wouldn't worry about this for now -- get graphics working first):


The stack

Klarth did a good job explaining the basics of what the stack is.

Only other thing worth noting is that the NES pushes values to the stack to "remember" where you came from when you do a JSR (or when an interrupt happens).  That way when you RTS/RTI, the CPU will pop values off the stack to know where to jump back to.


BIT is an awkward and not frequently used instruction.  The way it's being used in that code, LDA would do the same thing and would be much, much less confusing for newbies.  My advice is to replace the BIT with an LDA in the code, and don't worry about understanding BIT until you're more comfortable with the system.

BPL is a branch that examines the 'N' status flag.  Most instructions will modify 'N' according to the high bit (bit 7:  "$80") of the resulting value.  For example, with LDA (and BIT) if the high bit of the value you read was set, so too will the N flag be set.  Likewise if the high bit was cleared, so too will the N flag be cleared.

BPL will branch (jump) if the N flag is clear.

Therefore this loop:
Code: [Select]
  LDA $2002     ; (or BIT $2002)
  BPL vblankwait 1

Is effectively polling the $2002 register (aka PPUSTATUS) hundreds of times to examine the high bit (VBlank flag)... and keeps looping until that flag is set, at which point N will be set and the loop will exit.

This is a common way to wait for VBlank during startup -- but don't rely on it at any other time, as the VBlank flag is unreliable.  When you get to the point where VBlank matters -- you'll want to use NMIs.  However, don't worry about that yet -- just get something drawing first.


  • RHDN Patreon Supporter!
  • Hero Member
  • *****
  • Posts: 939
    • View Profile
Re: ELI5: Nerdy Nights Week 3
« Reply #3 on: June 29, 2017, 09:31:13 am »
Thanks, I think it makes more sense now.

So the addresses currently being written to here in bank 0 are mostly i/o lines for talking to APU/PPU then?

The ram gets loaded with garbage on boot so it needs cleaned out with clrmem before use.

And IRQs/NMIs are interrupt messages from other hardware to CPU, to execute different code and then return to normal business afterward.

I didn't notice the PPU Mask because I saw this here in the tutorial and my brain just kind of glazed over, I see it now though.

PPUMASK ($2001)

|||||||+- Grayscale (0: normal color; 1: AND all palette entries
|||||||   with 0x30, effectively producing a monochrome display;
|||||||   note that colour emphasis STILL works when this is on!)
||||||+-- Disable background clipping in leftmost 8 pixels of screen
|||||+--- Disable sprite clipping in leftmost 8 pixels of screen
||||+---- Enable background rendering
|||+----- Enable sprite rendering
||+------ Intensify reds (and darken other colors)
|+------- Intensify greens (and darken other colors)
+-------- Intensify blues (and darken other colors)

Doing morning practice sessions now to review what I've picked up,
So far memorized all the basics from Week 3, but some addresses and values still escape me so I'm working on that.

Just spent an hour poring over the code trying to find out why my asm wouldn't compile, only to find that I tried to INX x :banghead:

« Last Edit: June 30, 2017, 02:51:18 pm by SleepyFist »


  • RHDN Patreon Supporter!
  • Hero Member
  • *****
  • Posts: 939
    • View Profile
Re: ELI5: Nerdy Nights Week 3
« Reply #4 on: August 02, 2017, 11:42:32 am »
Can anybody tell me if it's okay to write out my palettes this way?
   .bank 1
   .org $E000
   .db $0f,$31,$32,$33;BG1;
   .db $0f,$31,$32,$33;BG2;
   .db $0f,$31,$32,$33;BG3;
   .db $0f,$31,$32,$33;BG4;
   .db $0f,$31,$32,$33;SP1;
   .db $0f,$31,$32,$33;SP2;
   .db $0f,$31,$32,$33;SP3;
   .db $0f,$31,$32,$33;SP4;

I did this for readability because my eyes have trouble tracking large strings of numbers but I'm afraid that it might be a waste of ROM space giving each one a separate entry? Not sure if it would be better to write them out individually and then combine into a single row or not.

I'd also like to know why 0D is considered a bad color, Nerdy Nights doesn't go into it, but I found a conversation where they were discussing it confusing certain TVs when used with all emphasis bits on.

Still working my way up the series slowly trying to memorize basics as I go.
« Last Edit: August 02, 2017, 12:35:55 pm by SleepyFist »


  • Hero Member
  • *****
  • Posts: 2814
  • NES Junkie
    • View Profile
Re: ELI5: Nerdy Nights Week 3
« Reply #5 on: August 02, 2017, 12:54:39 pm »
Can anybody tell me if it's okay to write out my palettes this way?

Yeah that's fine.  Things like that don't change the binary... the assembler will make the exact same file whether it's all on one line or spread out to several lines.  Whitespace, variable names, etc -- stuff like that gets dropped in the assembling process.

I'd also like to know why 0D is considered a bad color

To my understanding, CRT screens use a special frequency to mark where the end of the line is.  Color $0D gets a little too close to that frequency and can confuse some screens, causing the resulting image to screw up.

Though I'm sure there's a more technical/accurate explanation on the nesdev wiki if you want to know specifics.


  • RHDN Patreon Supporter!
  • Hero Member
  • *****
  • Posts: 939
    • View Profile
Re: ELI5: Nerdy Nights Week 3
« Reply #6 on: August 02, 2017, 02:29:46 pm »
Thanks. :thumbsup:


  • RHDN Patreon Supporter!
  • Hero Member
  • *****
  • Posts: 939
    • View Profile
Re: ELI5: Nerdy Nights Week 3
« Reply #7 on: October 03, 2017, 05:10:23 pm »
Hey-o, I just switched to a fresh setup with ASM6 as my compiler but I'm having trouble with it from the outset,
I feed it my asm directly
;Pin-Pong: Cosmic Horror Noir RPG;
   .inesprg 1
   .ineschr 1
   .inesmap 0
   .inesmir 1
   .bank 00
   .org $8000
   LDX #$40
   STX $4010
   LDX #$ff
   STX $2000
   STX $2001
   STX $4017
   JSR vwait
   LDA #$00
   STA $0000,x
   STA $0100,x
   STA $0200,x
   STA $0300,x
   STA $0400,x
   STA $0500,x
   STA $0600,x
   STA $0700,x
   CPX #$00
   BNE clrmem
   JSR vwait
   LDA #%00000000
   STA $2000
   LDA #%10000000
   STA $2001
   LDA $2002
   BPL vwait
   .bank 01
   .org $FFFA
   .dw NMI
   .dw RESET
   .dw 0
   .bank 02
   .org $0000
   .incbin "gfx.bin"
   ;Why doesn't this compile?;

and it spits out nothing, no .nes nothing, not even an error, there's some text that flashes by in an instant.

Next I tried running through a batch script

start asm6.exe Pong.asm Pong.nes

still nothing, there might be an acknowledgement that the program opened but that's it.

Anybody know what I'm doing wrong? Is there some bad habit I picked up that's meshing badly with asm6?

Okay, I tried to load in some code from when I was messing in NESASM3,
and among other things, it seems to be upset about my ines headers for some reason, not sure why though.
C:\Users\Doctor Velious\Desktop\PinPong>asm6.exe 01_Mario.asm Mario.nes
pass 1..
01_Mario.asm(2): Illegal instruction.(2-5 are ines headers)
01_Mario.asm(3): Illegal instruction.
01_Mario.asm(4): Illegal instruction.
01_Mario.asm(5): Illegal instruction.
01_Mario.asm(10): Illegal instruction.(bank 0)
01_Mario.asm(166): Illegal instruction.(bank 1)
01_Mario.asm(250): Illegal instruction.(bank 2)

C:\Users\Doctor Velious\Desktop\PinPong>pause
Press any key to continue . . .
« Last Edit: October 03, 2017, 08:56:51 pm by SleepyFist »