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:
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.