== Using the NMI - VBLANK ==

Get the COMPLETE source code HERE.

Earlier we made an interrupt table which assigned addresses for the SNES
interrupts. We assigned the address to an empty handler that just returned
for every interrupt except the NMI (VBLANK) interrupt. This interrupt occurs
whenever the T.V.'s tracer is not tracing to the screen, making it safe to
edit data in VRAM and update the screen. Not waiting for VBLANK can create
awful results on the screen, such as flicker. To illustrate how this interrupt
works, we'll increment the palette bits of a tile to make it flash different
colors. This is a cool effect :).

Notes: You should preserve most of your main registers in your NMI routine.
Also, you really shouldn't do anything not graphic-related, It's a waste of
precious time. VBLANK doesn't last forever, so you only have ample time
to do what you need to do. It's still plenty of time though, believe me.

The NMI interrupt is disabled at first, so we need to enable it. Enable it
through register $4200:

-----------------------------------------------------------------------------
Register $4200: Counter Enable

n-vh---j        n: NMI interrupt enable         v: vertical counter enable
                h: horizontal counter enable    j: joypad enable

We're only conscerned with the n bit, so just write $80 to this register to
enable the NMI.
-----------------------------------------------------------------------------
Register $4210 is also of importance (I think):
-----------------------------------------------------------------------------
Register $4210: NMI Register
x---vvvv:                       x: Disable/enable NMI.
                                v: Version # ($5A22 (???))

Bit 7 can be reset to 0 by reading this register. I can't really find much information on this register, but I see commercial
games and nice demos read this register during their VBLANK routines. Perhaps
it only disables NMI while you're in the NMI routine? I'm not positive, but just
go along and read the register in your VBlank routine, it can't hurt. If you
have any information on this register I'd like to hear about it. Contact me.

-----------------------------------------------------------------------------
WAI -

Note: In this code, the Stall macro I made WAI's 7 times. This has the main loop
run every 7 VBLANKS. This was my lazy way of slowing the fps down, so normally you
would only WAI once, remember that.

Let's Code!

------------------------------ Start NMI.asm --------------------------------
;------------------------------------------------------------------------
;- Written by: Bazz
;-    This code introduces the programmer to the VBLANK interrupt, and
;-    teaches him/her its powers. This should be pretty simple & easy
;-    to go through.
;------------------------------------------------------------------------

;== Include MemoryMap, HeaderInfo, and interrupt Vector table ==
.INCLUDE "header.inc"

;== Include Library Routines ==
.INCLUDE "InitSNES.asm"
.INCLUDE "LoadGraphics.asm"

;== EQUates ==
.EQU PalNum $0000       ; Use some RAM

;==========================================
; Main Code
;==========================================

.MACRO Stall
    .REPT 7
        WAI
    .ENDR
.ENDM

.BANK 0 SLOT 0
.ORG 0
.SECTION "MainCode"

Start:
    InitSNES

    rep #$10
    sep #$20
    
    stz PalNum

    LoadPalette BG_Palette, 0, 14
    LoadBlockToVRAM Tiles, $0000, $0020
    
    lda #$80
    sta $2115
    ldx #$0400
    stx $2116
    lda #$01
    sta $2118

    jsr SetupVideo
    
    lda #$80
    sta $4200       ; Enable NMI

Infinity:
    Stall

    lda PalNum
    clc
    adc #$04
    and #$0C        ; If > palette starting color > 24 (00011100), make 0
    sta PalNum

_done:
    JMP Infinity

;============================================================================
VBlank:
    rep #$10        ; X/Y=16 bits
    sep #$20        ; A/mem=8 bit    
    
    stz $2115       ; Setup VRAM
    ldx #$0400
    stx $2116       ; Set VRAM address
    lda PalNum
    sta $2119       ; Write to VRAM

    lda $4210       ; Clear NMI flag
    
    RTI
;============================================================================

;============================================================================
; SetupVideo -- Sets up the video mode and tile-related registers
;----------------------------------------------------------------------------
; In: None
;----------------------------------------------------------------------------
; Out: None
;----------------------------------------------------------------------------
SetupVideo:

    lda #$00
    sta $2105           ; Set Video mode 1, 8x8 tiles, 16 color BG1/BG2, 4 color BG3

    lda #$04            ; Set BG1's Tile Map offset to $0400 (Word address)
    sta $2107           ; And the Tile Map size to 32x32

    stz $210B           ; Set BG1's Character VRAM offset to $0000 (word address)

    lda #$01            ; Enable BG1
    sta $212C

    lda #$0F
    sta $2100           ; Turn on screen, full Brightness

    rts

.ENDS
;============================================================================

.BANK 1 SLOT 0
.ORG 0
.SECTION "CharacterData"

Tiles:
    .DW $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000
    .DB $00,$00,$24,$00,$24,$00,$24,$00
    .DB $00,$00,$81,$00,$FF,$00,$00,$00

BG_Palette:
    .DB $00, $00, $FF, $03
    .DW $0000, $0000, $0000
    .DB $1F, $00
    .DW $0000, $0000, $0000
    .DB $E0, $5D
    .DW $0000, $0000, $0000
    .DB $E0, $02

.ENDS
-------------------------------- End NMI.asm --------------------------------

See how you don't even have to call VBlank.. The interrupt take cares of all
that, because we told the SNES where to go when the NMI interrupt occurred
back in the header file. I want to explain some things before this section ends
1) The SetupVideo routine is the same as in the previous tutorial.

2) I'm 98% sure that modification of the Processor Status Register in
the interrupt routine stays in the interrupt routine.

3) The palette-changing code:

4) A logical AND takes two values, and compares them in a certain way. If
both bits in a certain spot are set, then the result is 1. Any other
combination results in an output of 0. Here's an example:
            10011011
        AND 11001001
        -------------
            10001001

If you set all the bits you want to compare with to 1, you create a
sort of mask, that will only output 1's when the value compared with
has bits set in those placeholders. In our case,

            00011100
        AND 00001000
        -------------
            00001000

So this will output the same value everytime, UNTIL it goes over that
value and has no bits set in the masked place holders. So when I go
over 28 (00011100), I'll be in 32, because I'm incrementing by 4.
Watch what happens when I AND the mask to 32.

            00011100
        AND 00100000
        -------------
            00000000

It resets! So it 'auto-checks' itself. Pretty cool, especially if you
want some nice, fast and efficient code (we're coding in asm anyways,
which is fast in its own right). If you ever want to fashion something
in this way, just message me and I'll try to help you out.
I could have just CMP #xx, BNE or BEQ'ed to some place, but I
thought of doing this by accident actually. Hell, now you you know
another way to speed up your code.

Well, besides seeing that way of using AND, this code should have been easy to
follow. Enjoy.

Get the COMPLETE source code HERE.

back