Page 1 of 5
SNES dev newbie questions
Posted: Fri Aug 19, 2011 2:11 pm
by juef
Hey all,
I've just started fiddling around with SNES stuff, more specifically with the
Wikibooks tutorial. I have some (probably very) basic questions for which I haven't found an answer on these boards (or byuu's); if I am supposed to find the answers by more research or by moving forward, please don't hesitate to me so. Here's a basic
change the BG color example I will refer to in my questions:
Code: Select all
.include "Header.inc"
.include "Snes_Init.asm"
VBlank:
RTI
.bank 0
.section "MainCode"
Start:
Snes_Init
; Set the background color to green.
sep #$20 ; Set the A register to 8-bit.
lda #%10000000 ; Force VBlank by turning off the screen.
sta $2100
lda #%11100000 ; Load the low byte of the green color.
sta $2122
lda #%00000000 ; Load the high byte of the green color.
sta $2122
lda #%00001111 ; End VBlank, setting brightness to 15 (100%).
sta $2100
Forever:
jmp Forever
.ends
- Is there a way to load both bytes into $2122 at once? It seems kinda strange to me to set the register to 8 bits, load 8 bits, then load 8 more bits, rather than doing all of this with just one lda/sta.
- Speaking of which... How come doing sta $2122 twice with a byte both times doesn't end up just writing the latest byte of the two? Is writing the low byte first, and the the high byte the "standard way" of setting a color value here?
- I tried copying and pasting the "Set the background [...]" block so that there's two of them, changing the color to red the second time. By moving the Forever label at the top of the first of these blocks, I expected alternating red and green as the background color, but I end up with a rather trippy effect involving red and black only. Am I supposed to do something between (or during) VBlanks?
Thanks a bunch for reading, and big thanks to all the passionnate and helpful people around here - I admire all you've done and are doing for the community and beyond!

Re: SNES dev newbie questions
Posted: Sun Aug 21, 2011 3:44 am
by koitsu
juef wrote:Code: Select all
; Set the background color to green.
sep #$20 ; Set the A register to 8-bit.
lda #%10000000 ; Force VBlank by turning off the screen.
sta $2100
lda #%11100000 ; Load the low byte of the green color.
sta $2122
lda #%00000000 ; Load the high byte of the green color.
sta $2122
lda #%00001111 ; End VBlank, setting brightness to 15 (100%).
sta $2100
Is there a way to load both bytes into $2122 at once? It seems kinda strange to me to set the register to 8 bits, load 8 bits, then load 8 more bits, rather than doing all of this with just one lda/sta.
No. $2122 is an 8-bit memory-mapped register for colour data, the data of which is 16-bits; each write to it is handled internally by the PPU unit on the system. There's no "direct" 16-bit equivalent. If you were to write a 16-bit value to $2122, the LSB would end up going into $2122, and the MSB would end up going into $2123 which is a completely different register (window mask setting).
Per my old SNES documentation:
Code: Select all
| w |$2121 |Colour # (or pallete) selection register [CGADD] |
| | |xxxxxxxx x: Address (color #). |
| | | |
| | | |
| wd |$2122 |Colour data register [CGDATA] |
| | |xxxxxxxx x: Value of colour. |
| | | |
| | |SNES colour is 15 bit; 5 bits for red, green, and blue. The |
| | |order isn't RGB though: It's BGR (RGB reversed!). |
I should note you aren't setting $2121 to the index/offset of the palette entry you wish to modify prior to doing it -- so I assume your init_snes routine is what's responsible. I have to assume you're modifying colour #0. Just an FYI.
juef wrote:Speaking of which... How come doing sta $2122 twice with a byte both times doesn't end up just writing the latest byte of the two? Is writing the low byte first, and the the high byte the "standard way" of setting a color value here?
Because it's an 8-bit memory-mapped register. Each write is handled by the PPU. You should probably find a copy of my old SNES documentation and review the SNES.1 file. Both bytes written via the STA statements are honoured by the PPU/used by VRAM.
juef wrote:I tried copying and pasting the "Set the background [...]" block so that there's two of them, changing the color to red the second time. By moving the Forever label at the top of the first of these blocks, I expected alternating red and green as the background color, but I end up with a rather trippy effect involving red and black only. Am I supposed to do something between (or during) VBlanks?
What's happening is that you're updating the screen "too fast" and not seeing what's going on (visually). This is further complicated by your desire to turn on/off VBlank.
You have two options, and I recommend the former:
1) Leave the screen on at all times/set brightness to 100% and do your screen updates (writes to $2122) inside of VBlank time (NMI). You can wait for the NMI by examining bit 7 of $4210. You'll need to tie VBlank to NMI by setting bit 7 of $4200 as well. You can use the WAI opcode to wait for NMI rather than have to poll $4210.
2) Leave the screen on at all times/set brightness to 100% and do everything in your main loop but instead of disabling VBlank, you need to wait for it. See bit 7 of $4212.
In either case you need to add some delays. The screen is updated at 60 times a second (50 if PAL). The easiest way to delay is to wait for VBlank repetitively (more waits = more delay). I used to use something like this:
Code: Select all
rep #$10
ldx #200 ; Higher value = longer delay
delay jsr WaitVBL
dex
bpl delay
sep #$10
...
;
; Note: Should make sure accum is 8-bit here, not 16-bit
;
WaitVBL:
pha
xxx lda $4210
bpl xxx
lda $4210 ; Reset it
pla
rts
It might be worthwhile, as I said earlier, to download my old SNESdoc. Inside of that archive is an another archive called TEST.LZH, which contains some source code to a very small demo used to show off some of the features on the SNES. The code is simple and should get you started, and it reads fairly easily.
Re: SNES dev newbie questions
Posted: Sun Aug 21, 2011 5:11 am
by juef
koitsu wrote:It might be worthwhile, as I said earlier, to download my old SNESdoc. Inside of that archive is an another archive called TEST.LZH, which contains some source code to a very small demo used to show off some of the features on the SNES. The code is simple and should get you started, and it reads fairly easily.
I have found your docs; they seem rather helpful and complete. Thanks for writing them, I definitely have to read a bit more before continuing. But most of all, thank you very much for your reply: it is much more complete and helpful than what I hoped!
Posted: Tue Aug 23, 2011 7:50 am
by gilligan
As much as koitsu/yoshi deserves credit for his snes documentation which were released /long/ ago you should now refer to more complete documentation most of which can be found at
http://romhacking.net or possibly
http://wiki.superfamicom.org/ First and foremost you want to grab
http://www.romhacking.net/docs/196/ which is a detailed description of all registers including additional info. Looking around on romhacking you will also find an (most certainly illegal) copy of the complete snes development manuals as issued by nintendo. I do have a printed copy of those but tbh the txt file mentioned before is much more useful.
Hope that helps. Enjoy!
Posted: Wed Aug 24, 2011 7:18 pm
by juef
This is probably more of a general assembly question, but I can't figure this one out. I tried to isolate the issue, which is why this code is missing a lot of important stuff:
Code: Select all
.include "header.inc"
.include "Snes_Init.asm"
VBlank:
RTI
.bank 0
.section "MainCode"
Start:
Snes_Init
sep #$20
;TestTable:
; .db $00
Forever:
lda #%00011111
sta $2122
lda #%00000000
sta $2122
lda #%00001111
sta $2100
jmp Forever
.ends
As it is above, the code colors the screen red. What confuses me is that uncommenting the two TestTable lines makes the screen green instead. I don't refer to this table anywhere, so I guess somehow adding this piece of code messes with the registers?
Also, thanks for the help gilligan, I've checked those links out a little. Very helpful indeed!
Posted: Wed Aug 24, 2011 7:40 pm
by thefox
juef wrote:What confuses me is that uncommenting the two TestTable lines makes the screen green instead. I don't refer to this table anywhere, so I guess somehow adding this piece of code messes with the registers?
That is because it executes the data as code since you placed it in the middle of code. Move it to a place which execution will never reach, such as below the "jmp Forever" instruction.
Posted: Thu Sep 01, 2011 7:17 pm
by juef
Thank you for the reply, thefox! There's a lot of stuff I clearly haven't understood correctly yet, and that's one less of them now.
As an effort to understand better what I'm doing exactly, I've been trying to convert my source code to something that byuu's bass can compile. It's been easier than I thought, except for just this part:
Code: Select all
.SNESHEADER
; [...] some useless header stuff happens here (it can be commented without problem)
LOROM
; [...] some useless header stuff happens here (it can be commented without problem)
.ENDSNES
.SNESNATIVEVECTOR ; Define Native Mode interrupt vector table
COP EmptyHandler
BRK EmptyHandler
ABORT EmptyHandler
NMI VBlank
IRQ EmptyHandler
.ENDNATIVEVECTOR
.SNESEMUVECTOR ; Define Emulation Mode interrupt vector table
COP EmptyHandler
ABORT EmptyHandler
NMI EmptyHandler
RESET Start ; where execution starts
IRQBRK EmptyHandler
.ENDEMUVECTOR
This is part of a 'header.inc' file that my tutorial uses. When assembled, it writes some code at the end, without which the program cannot run. For now, just copying the assembled code from WLA into the file bass assembles works just fine. I'm not too sure what it that code is/does, but mostly, I'd be interested in knowing how to
translate it to bass. I'm guessing a different syntax will provide me another way of understanding it, as has happened with plenty of other stuff in the conversion so far.
Thank you all again very much!
Posted: Thu Sep 01, 2011 8:04 pm
by Hamtaro126
juef wrote:Thank you for the reply, thefox! There's a lot of stuff I clearly haven't understood correctly yet, and that's one less of them now.
As an effort to understand better what I'm doing exactly, I've been trying to convert my source code to something that byuu's bass can compile. It's been easier than I thought, except for just this part:
Refer to MukundaJay's SNES Devkit '09, for CA65.
http://snes.mukunda.com/
Header examples can be found there, More portable than WLA-DX's
or at least steal the header from your favorite ROM (at least FFC0-FFFF) such as Super Mario World (That will be terrible, though).
Posted: Thu Sep 01, 2011 8:21 pm
by Memblers
That isn't code at all, but it just a list of address to your interrupt routines. I guess this was considered significant enough to have it's own Wikipedia article:
http://en.wikipedia.org/wiki/Interrupts ... processors
Normally you would use .word Emptyhandler (or in CA65 .addr EmptyHandler is the same thing). You can place this stuff manually with the .org command, but it seems like assemblers usually provide some kind of way of defining it.
Posted: Fri Sep 02, 2011 3:06 am
by Near
I don't provide additional markup, as it's really not necessary.
At the top of the file is all you need for LoROM addressing.
As for the vectors, write them directly:
Code: Select all
org $ffe4; dw COP
org $ffe6; dw BRK
org $ffea; dw NMI
org $ffee; dw IRQ
org $fffc; dw Reset
Those are just labels to code that appears between $008000 and $00ffff. So Reset would be Start in your case. You don't HAVE to define ones you don't ever use.
You'll never use the emulation-mode ones.
Posted: Sat Sep 03, 2011 10:50 am
by juef
Thanks you all for your answers! Now I can compile what I had done so far with bass, with which I'll probably be stickin' from now on!
Posted: Tue Sep 13, 2011 5:30 pm
by juef
I have been asked to share my code that compiles with bass. I have progressed beyond what I'm posting below, but as there's still some stuff I can't figure out and I won't have the time to look at it for a few days, I'll just share what I had successfully compiled in my previous post. All that happens is that the screen gets colored red.
File "test.asm"
Code: Select all
incsrc "SnesInit.asm"
Start:
{SnesInit}
lda #%00011111
sta $2122
lda #%00000000
sta $2122
lda #%00001111
sta $2100
Forever:
jmp Forever
File "SnesInit.asm"
Code: Select all
mapper lorom
org $8000; fill $010000
org $ffea; dw EmptyHandler // SNES vectors
org $fffc; dw Start
org $8000;
macro SnesInit;
sei; // Disabled interrupts
clc; // clear carry to switch to native mode
xce; // Xchange carry & emulation bit. native mode
rep #$18; // Binary mode (decimal mode off), X/Y 16 bit
ldx #$1FFF; // set stack to $1FFF
txs;
jsr Init;
endmacro
Init:
sep #$30 // X,Y,A are 8 bit numbers
lda #$8F // screen off, full brightness
sta $2100 // brightness + screen enable register
stz $2101 // Sprite register (size + address in VRAM)
stz $2102 // Sprite registers (address of sprite memory [OAM])
stz $2103 // "" ""
stz $2105 // Mode 0, = Graphic mode register
stz $2106 // noplanes, no mosaic, = Mosaic register
stz $2107 // Plane 0 map VRAM location
stz $2108 // Plane 1 map VRAM location
stz $2109 // Plane 2 map VRAM location
stz $210A // Plane 3 map VRAM location
stz $210B // Plane 0+1 Tile data location
stz $210C // Plane 2+3 Tile data location
stz $210D // Plane 0 scroll x (first 8 bits)
stz $210D // Plane 0 scroll x (last 3 bits) #$0 - #$07ff
lda #$FF // The top pixel drawn on the screen isn't the top one in the tilemap, it's the one above that.
sta $210E // Plane 0 scroll y (first 8 bits)
sta $2110 // Plane 1 scroll y (first 8 bits)
sta $2112 // Plane 2 scroll y (first 8 bits)
sta $2114 // Plane 3 scroll y (first 8 bits)
lda #$07 // Since this could get quite annoying, it's better to edit the scrolling registers to fix this.
sta $210E // Plane 0 scroll y (last 3 bits) #$0 - #$07ff
sta $2110 // Plane 1 scroll y (last 3 bits) #$0 - #$07ff
sta $2112 // Plane 2 scroll y (last 3 bits) #$0 - #$07ff
sta $2114 // Plane 3 scroll y (last 3 bits) #$0 - #$07ff
stz $210F // Plane 1 scroll x (first 8 bits)
stz $210F // Plane 1 scroll x (last 3 bits) #$0 - #$07ff
stz $2111 // Plane 2 scroll x (first 8 bits)
stz $2111 // Plane 2 scroll x (last 3 bits) #$0 - #$07ff
stz $2113 // Plane 3 scroll x (first 8 bits)
stz $2113 // Plane 3 scroll x (last 3 bits) #$0 - #$07ff
lda #$80 // increase VRAM address after writing to $2119
sta $2115 // VRAM address increment register
stz $2116 // VRAM address low
stz $2117 // VRAM address high
stz $211A // Initial Mode 7 setting register
stz $211B // Mode 7 matrix parameter A register (low)
lda #$01
sta $211B // Mode 7 matrix parameter A register (high)
stz $211C // Mode 7 matrix parameter B register (low)
stz $211C // Mode 7 matrix parameter B register (high)
stz $211D // Mode 7 matrix parameter C register (low)
stz $211D // Mode 7 matrix parameter C register (high)
stz $211E // Mode 7 matrix parameter D register (low)
sta $211E // Mode 7 matrix parameter D register (high)
stz $211F // Mode 7 center position X register (low)
stz $211F // Mode 7 center position X register (high)
stz $2120 // Mode 7 center position Y register (low)
stz $2120 // Mode 7 center position Y register (high)
stz $2121 // Color number register ($0-ff)
stz $2123 // BG1 & BG2 Window mask setting register
stz $2124 // BG3 & BG4 Window mask setting register
stz $2125 // OBJ & Color Window mask setting register
stz $2126 // Window 1 left position register
stz $2127 // Window 2 left position register
stz $2128 // Window 3 left position register
stz $2129 // Window 4 left position register
stz $212A // BG1, BG2, BG3, BG4 Window Logic register
stz $212B // OBJ, Color Window Logic Register (or,and,xor,xnor)
sta $212C // Main Screen designation (planes, sprites enable)
stz $212D // Sub Screen designation
stz $212E // Window mask for Main Screen
stz $212F // Window mask for Sub Screen
lda #$30
sta $2130 // Color addition & screen addition init setting
stz $2131 // Add/Sub sub designation for screen, sprite, color
lda #$E0
sta $2132 // color data for addition/subtraction
stz $2133 // Screen setting (interlace x,y/enable SFX data)
stz $4200 // Enable V-blank, interrupt, Joypad register
lda #$FF
sta $4201 // Programmable I/O port
stz $4202 // Multiplicand A
stz $4203 // Multiplier B
stz $4204 // Multiplier C
stz $4205 // Multiplicand C
stz $4206 // Divisor B
stz $4207 // Horizontal Count Timer
stz $4208 // Horizontal Count Timer MSB (most significant bit)
stz $4209 // Vertical Count Timer
stz $420A // Vertical Count Timer MSB
stz $420B // General DMA enable (bits 0-7)
stz $420C // Horizontal DMA (HDMA) enable (bits 0-7)
stz $420D // Access cycle designation (slow/fast rom)
cli // Enable interrupts
rts
EmptyHandler:
rti
Re: SNES dev newbie questions
Posted: Thu Feb 27, 2014 7:30 pm
by juef
Sorry to bring back such an old topic; I know it's been a long time but I've felt the desire to go back to these things recently and the above posts are still relevant to my following questions.
1) In the SNES init routine above, there is this one line:
Code: Select all
stz $4200 // Enable V-blank, interrupt, Joypad register
Now, from what I understand, this sets $4200 to zero, which... disables VBlank NMI, right? Shouldn't this be the following instead?
2) Following koitsu's above advice, I tried inplementing delays between my background color change. The main code goes like this: init the SNES, set the background green, wait, set it red, dim the screen a little, and then do nothing forever. The screen is indeed dimmed, but the color is still red. Any clue to why I can't get some red?
Code: Select all
Snes_Init
sep #$20
lda #%10000000
sta $2100
lda #%11100000
sta $2122
lda #%00000011
sta $2122
lda #%00001111
sta $2100
rep #$10
ldx #200
delay:
jsr WaitVBL
dex
bpl delay
sep #$10
sep #$20
lda #%10000000
sta $2100
lda #%00011111
sta $2122
lda #%00000000
sta $2122
lda #%00000100
sta $2100
Forever:
jmp Forever
WaitVBL:
sep #$20
pha
wai
pla
rts
(Note that I understand the advice about keeping the screen on, but I tried this as a test when I couldn't get red.)
This is with WLADX, as I was unable to make my code suitable for the latest bass version.
Any help would be much appreciated! Thank you!
Re: SNES dev newbie questions
Posted: Thu Feb 27, 2014 9:59 pm
by koitsu
I don't see you setting $2121 anywhere, which means you're just blindly appending onto the end of the palette rather than changing existing on-screen colours. You probably want an stz $2121 before tinkering with $2122.
As for the // Enable V-blank, interrupt, Joypad register comment: the comment itself is wrong and you are correct -- stz $4200 would DISABLE NMI when VBlank fires, not enable. You really should turn on NMI when VBlank fires, otherwise your wai in WaitVBL doesn't serve a purpose.
Also, WaitVBL doesn't look quite right (you could replace that whole thing with just wai ; rts and be done with it; I don't see the point behind the stack operations or the sep #$20). The way the routine "waits for VBlank", BTW, is by waiting for NMI to fire using wai, which you want tied to VBlank, hence what I said in my above paragraph. :-)
You could also replace Forever: jmp Forever with just stp (STop Processor) if you wanted.
Re: SNES dev newbie questions
Posted: Fri Feb 28, 2014 3:34 am
by DoNotWant
Upload your project somewhere in a rar file, and I can make it into bass v14 code for you, and put some comments in there of what to think of when using bass. I'm no expert, but I got some stuff working with it.