Why no SNES homebrew scene?

Discussion of hardware and software development for Super NES and Super Famicom.

Moderator: Moderators

Forum rules
  • For making cartridges of your Super NES games, see Reproduction.
tepples
Posts: 22278
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Why no SNES homebrew scene?

Post by tepples » Fri Aug 04, 2017 6:07 am

Sumez wrote:sure there are some nice examples of NES games created in C, but I'm sure we can all agree on the massive overhead it creates
And any fan of Koei war sims would consider the overhead worth it.

Now as I find time, I could help solve "g. Less known working demo source code." Now that I've started on my own music engine S-Pently, I plan to do more on this system.

But I'm still drawing a blank for "c-d. Filling 15 colors and 3 layers". Bregalad makes a good point about solo projects. With SMW art quality being the low bar on Super NES, solo developers scale their plans back to the NES, where solo art is more acceptable. Progressing past a solo project requires interpersonal skills to interest pixel artists in your project, which can prove difficult with the autism endemic among hobbyist programmers.

So I guess I have to demonstrate these issues myself by porting several of my old NES projects to Super NES. It'll add some working source code for other programmers to look at. But it'll also show how out-of-place NES-grade solo artwork looks on Super NES outside Williams Arcade's Greatest Hits.

User avatar
MottZilla
Posts: 2835
Joined: Wed Dec 06, 2006 8:18 pm

Re: Why no SNES homebrew scene?

Post by MottZilla » Fri Aug 04, 2017 10:42 am

I would agree that artwork and music/dealing with the SPC are the biggest challenges for doing something on the SNES. The standards for audio and visual quality on the SNES can be pretty high among players when compared to the top licensed games.

But if you can get the right people involved that are serious about a project and can devote the time to it, I don't think the SNES is significantly less accessible than similar platforms. Homebrew and licensed games can both suffer from poor quality due to lack of knowledge or time. Of course other platforms can make things easier like the PC since anyone that can make artwork or music in whatever format you can probably convert and use.

To the original topic, I wouldn't say there is "no homebrew scene" for the SNES. There's plenty of homebrew and hacks out there for the SNES.

User avatar
Sumez
Posts: 920
Joined: Thu Sep 15, 2016 6:29 am
Location: Denmark (PAL)

Re: Why no SNES homebrew scene?

Post by Sumez » Fri Aug 04, 2017 4:59 pm

There's plenty of hacks (which really isn't homebrew, even if a few more creative ones are borderline), and maybe a couple of actual homebrews. :P But is there any "scene" apart from what's present here?

Erockbrox
Posts: 384
Joined: Sun Nov 23, 2014 12:16 pm

Re: Why no SNES homebrew scene?

Post by Erockbrox » Fri Aug 04, 2017 7:09 pm

It would be really great if someone made a easy to use snes game engine and tools for SNES development. And while I know that there are tools and programs that exists it just seems like there really isn't any community of people doing it.

However if you look at some of the SNES hacking scenes, some of them are actually pretty advanced. For example the Super Metroid hacking scene has produced some good hacks. And the one scene which seems to dominate over all is the smw hacking scene. There are literally hundreds of custom sprites, hundreds of custom blocks and hundreds of custom level codes and patches. Combined with a top quality editor and lots of tools, you can literally make all sorts of new games using the smw game engine.

tepples
Posts: 22278
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Why no SNES homebrew scene?

Post by tepples » Fri Aug 04, 2017 7:55 pm

But the SMW hacks also contain substantially all the artwork from SMW. An all-new engine designed to make SMW hacking less necessary would need all-new artwork.

psycopathicteen
Posts: 2979
Joined: Wed May 19, 2010 6:12 pm

Re: Why no SNES homebrew scene?

Post by psycopathicteen » Fri Aug 04, 2017 9:12 pm

I've been busy today squeezing my animation engine down to as little lines of code as possible. I want it so short that noobies would think I wrote it in just a few minutes.

User avatar
Drew Sebastino
Formerly Espozo
Posts: 3503
Joined: Mon Sep 15, 2014 4:35 pm
Location: Richmond, Virginia

Re: Why no SNES homebrew scene?

Post by Drew Sebastino » Sat Aug 05, 2017 1:24 am

psycopathicteen wrote:@Espozo. When you program, do you start from scratch each time, or do you continue where you left off?
I start where I left off. I don't comment anything, but I give everything super-long, descriptive names. I just haven't been programming for a good long while now.
Erockbrox wrote:you can literally make all sorts of new games using the smw game engine.
It's just unfortunate, because SMW is a really shitty base. :?

Oh yeah, about audio, I've still never gotten around to touching it. I don't even know what responsibilities people generally give the SPC 700; I'd probably let the 65816 actually mix the songs and just send commands to the SPC700.

User avatar
HihiDanni
Posts: 186
Joined: Tue Apr 05, 2016 5:25 pm

Re: Why no SNES homebrew scene?

Post by HihiDanni » Sat Aug 05, 2017 7:51 am

Espozo wrote:
Erockbrox wrote:you can literally make all sorts of new games using the smw game engine.
It's just unfortunate, because SMW is a really shitty base. :?
I have to agree. Just looking at a RAM map of SMW is enough to make me want to shake my head. You're going to get more mileage out of a homebrew engine or even writing your own thing from scratch.
Espozo wrote:Oh yeah, about audio, I've still never gotten around to touching it. I don't even know what responsibilities people generally give the SPC 700; I'd probably let the 65816 actually mix the songs and just send commands to the SPC700.
This is inadvisable. There's a reason the SPC700 has high resolution timers and is mostly isolated from the rest of the system. It's basically an early example of threaded audio. You don't want to get stuck notes while the main CPU is busy doing other things. That's the sort of stuff you get on the NES and Genesis but is virtually non-existent on the SNES. IMHO, just figure out how the SPC upload protocol works, then toy around with SNESGSS or another existing homebrew sound driver. You're going to get results much faster that way.
SNES NTSC 2/1/3 1CHIP | serial number UN318588627

psycopathicteen
Posts: 2979
Joined: Wed May 19, 2010 6:12 pm

Re: Why no SNES homebrew scene?

Post by psycopathicteen » Sat Aug 05, 2017 7:02 pm

If I'm trying to make a code library for others to use, how much shorter do I need to make this code to not look intimidating?

Code: Select all

animation:
ldy {metasprite_request}
bne +				//no animation, if "metasprite_request" is blank
jsr clear_vram_slot
stz {frame_id}
rts
+;
lda {animation_update}		//sprite is animated if "metapsprite" is different from
bne +				//"metasprite_request" or "animation_update" is set
cpy {metasprite}
bne +
rts
+;
lda $000c,y
tax
clc
adc {total_dma_legnth}		//check if there is enough DMA time for sprite
cmp #$0081
bcc ++
lda {first_object_to_dma}
bne +
tdc
sta {first_object_to_dma}
+;
rts
+;
stx {vram_size}
jsr clear_vram_slot		//clear previous animation frame
stz {180_degrees_flip}
lda {animation_update}
cmp #$0002			//if "animation_update" is 2, then frame is rotated 180 degrees
bne +
lda #$c000
sta {180_degrees_flip}
+;
stz {animation_update}
ldy {metasprite_request}
sty {metasprite}

lda $000e,y
clc
adc {animation_index}
inc #2
sta {frame_id}
tax
lda {animation_copies},x
beq +				//if animation_copies is not 0, then no further processing
inc				//is needed
sta {animation_copies},x
rts
+;
inc
sta {animation_copies},x

lda $000a,y			//find the ROM address of sprite graphics
asl #5				//ROM address = metasprite ROM address + (animation frame)*(metasprite ROM size)
sta {vram_width}
sep #$20
lda {animation_index}
lsr
sta $4202
lda $000c,y
sta $4203
nop
rep #$20
lda $4216
asl #5
clc
adc $0006,y
sta {temp3}			//"temp3" is ROM address
lda $0008,y
sta {temp2}			//"temp2" is ROM bank
lda $0010,y
bpl +				//if $0010,y is $7fff or less, it is the metasprite data itself
tya				//if $0010,y is $8000 or more, it is the address pointing to metasprite data
clc
adc {animation_index}
tax
ldy $0010,x
+;
phd
lda #$0000
tcd
ldx #$ffff
-;
	lda $0010,y
	bne +
	txa
invalid_chr:
	pld
	ldx {frame_id}
	sta {animation_chr},x
	lda {vram_size}
	clc
	adc {total_dma_legnth}
	sta {total_dma_legnth}
	rts				//this is where the game exits the routine
	+;
	xba
	and #$000f
	sta.b {temp7}			//"temp7" counts down the vertical sprite run
	lda $0011,y
	and #$0070
	lsr #4
	sta.b {temp8}			//"temp8" counts down the horizontal sprite run
	lda $0018,y
	adc.b {temp3}
	sta.b {temp}				//"temp" is the ROM address of the sprite being added
	sta.b {temp9}				//"temp9" is the ROM address of the top sprite in a vertical run
	lda $0012,y
	sta.b {temp_x}
	lda $0014,y
	sta.b {temp_y}
	sty.b {temp5}
-;
		jmp find_vram_slot
find_vram_slot_done:
		ldy.b {temp5}			//this puts all the "OAM" information
		lda.b {temp6}			//onto the linked list
		ora $0016,y
		sta {sprite_attributes},x
		lda.b {temp4}
		sta {sprite_size},x
		asl
		tay
		lda.b {temp_x}
		sta {sprite_x},x
		lda.b {temp_y}
		sta {sprite_y},x
		dec.b {temp7}			//decreases "temp 7" until all sprites
		bmi +				//in the vertical run are put on linked list
		clc
		adc sprite_size_LUT,y
		sta.b {temp_y}
		ldy.b {temp5}
		bra -
+;
		dec.b {temp8}			//decreases "temp 8" until all sprites
		bmi +				//in the horizontal run are put on linked list
		lda.b {temp_x}
		clc
		adc sprite_size_LUT,y
		sta.b {temp_x}
		lda.b {temp9}
		clc
		adc sprite_size_LUT2,y
		sta.b {temp9}			//this moves the ROM address of the sprites
		sta.b {temp}			//to the next sprite in a horizontal run
		ldy.b {temp5}
		lda $0014,y
		sta.b {temp_y}
		lda $0011,y
		and #$000f
		sta.b {temp7}
		jmp -
+;
	lda.b {temp5}
	clc
	adc #$000a
	tay
	jmp --

sprite_size_LUT:
dw $0010,$0020,$0010,$0020
sprite_size_LUT2:
dw $0040,$0080,$0040,$0080

find_vram_slot:
lda $0010,y
txy
and #$0003
dec
beq small_slot
cmp #$0001
beq large_slot

repeat_slot:			//this finds repeat slots
tsb.b {temp4}
ldx {repeat_slot_stack_index}
dex
dex
bpl +
tya
jmp invalid_chr
+;
lda {repeat_slot_stack},x
stx {repeat_slot_stack_index}
tax
tya
sta {sprite_name},x		//puts it on linked list
jmp find_vram_slot_done

small_slot:			//this finds 16x16 VRAM slot
sta.b {temp4}
ldx {small_slot_stack_index}
dex
dex
bpl +
tya
jmp invalid_chr
+;
lda {small_slot_stack},x
stx {small_slot_stack_index}
tax
tya
sta {sprite_name},x		//adds slot to linked list
lda x16_lut,x
ldy.b {dma_updates}
sta {dma_destination},y		//sets up dma queue
clc
adc #$0100
sta {dma_destination}+8,y
lda.b {temp}
sta {dma_address},y
adc {vram_width}
sta {dma_address}+8,y
adc {vram_width}
sta.b {temp}
lda.b {temp2}
ora #$4000
sta {dma_bank},y
sta {dma_bank}+8,y
tya
clc
adc #$0010
sta.b {dma_updates}
stx.b {temp6}
jmp find_vram_slot_done

large_slot:			//finds open 32x32 VRAM slot
sta.b {temp4}
ldx {large_slot_stack_index}
dex
dex
bpl +
tya
jmp invalid_chr
+;
lda {large_slot_stack},x
stx {large_slot_stack_index}
tax
tya
sta {sprite_name},x		//adds slot to linked list
lda x16_lut,x
ldy.b {dma_updates}
sta {dma_destination},y		//sets up DMA queue
clc
adc #$0100
sta {dma_destination}+8,y
adc #$0100
sta {dma_destination}+16,y
adc #$0100
sta {dma_destination}+24,y
lda.b {temp}
sta {dma_address},y
adc {vram_width}
sta {dma_address}+8,y
adc {vram_width}
sta {dma_address}+16,y
adc {vram_width}
sta {dma_address}+24,y
adc {vram_width}
sta.b {temp}
lda.b {temp2}
ora #$8000
sta {dma_bank},y
sta {dma_bank}+8,y
sta {dma_bank}+16,y
sta {dma_bank}+24,y
tya
clc
adc #$0020
sta.b {dma_updates}
stx.b {temp6}
jmp find_vram_slot_done


x16_lut:
dw $0000,$0020,$0040,$0060,$0080,$00a0,$00c0,$00e0,$0110,$0120,$0140,$0160,$0180,$01a0,$01c0,$01e0
dw $0200,$0220,$0240,$0260,$0280,$02a0,$02c0,$02e0,$0310,$0320,$0340,$0360,$0380,$03a0,$03c0,$03e0
dw $0400,$0420,$0440,$0460,$0480,$04a0,$04c0,$04e0,$0510,$0520,$0540,$0560,$0580,$05a0,$05c0,$05e0
dw $0600,$0620,$0640,$0660,$0680,$06a0,$06c0,$06e0,$0710,$0720,$0740,$0760,$0780,$07a0,$07c0,$07e0
dw $0800,$0820,$0840,$0860,$0880,$08a0,$08c0,$08e0,$0910,$0920,$0940,$0960,$0980,$09a0,$09c0,$09e0
dw $0a00,$0a20,$0a40,$0a60,$0a80,$0aa0,$0ac0,$0ae0,$0b10,$0b20,$0b40,$0b60,$0b80,$0ba0,$0bc0,$0be0
dw $0c00,$0c20,$0c40,$0c60,$0c80,$0ca0,$0cc0,$0ce0,$0d10,$0d20,$0d40,$0d60,$0d80,$0da0,$0dc0,$0de0
dw $0e00,$0e20,$0e40,$0e60,$0e80,$0ea0,$0ec0,$0ee0,$0f10,$0f20,$0f40,$0f60,$0f80,$0fa0,$0fc0,$0fe0
dw $1000,$1020,$1040,$1060,$1080,$10a0,$10c0,$10e0,$1110,$1120,$1140,$1160,$1180,$11a0,$11c0,$11e0
dw $1200,$1220,$1240,$1260,$1280,$12a0,$12c0,$12e0,$1310,$1320,$1340,$1360,$1380,$13a0,$13c0,$13e0
dw $1400,$1420,$1440,$1460,$1480,$14a0,$14c0,$14e0,$1510,$1520,$1540,$1560,$1580,$15a0,$15c0,$15e0
dw $1600,$1620,$1640,$1660,$1680,$16a0,$16c0,$16e0,$1710,$1720,$1740,$1760,$1780,$17a0,$17c0,$17e0
dw $1800,$1820,$1840,$1860,$1880,$18a0,$18c0,$18e0,$1910,$1920,$1940,$1960,$1980,$19a0,$19c0,$19e0
dw $1a00,$1a20,$1a40,$1a60,$1a80,$1aa0,$1ac0,$1ae0,$1b10,$1b20,$1b40,$1b60,$1b80,$1ba0,$1bc0,$1be0
dw $1c00,$1c20,$1c40,$1c60,$1c80,$1ca0,$1cc0,$1ce0,$1d10,$1d20,$1d40,$1d60,$1d80,$1da0,$1dc0,$1de0
dw $1e00,$1e20,$1e40,$1e60,$1e80,$1ea0,$1ec0,$1ee0,$1f10,$1f20,$1f40,$1f60,$1f80,$1fa0,$1fc0,$1fe0

no_slot_to_clear:
rts

clear_vram_slot:	//this routine clears the VRAM slots of the previous animation frame
ldx {frame_id}
beq no_slot_to_clear
lda {animation_copies},x
beq +
dec
sta {animation_copies},x
bne no_slot_to_clear
+;
lda {animation_chr},x
-;
cmp #$ffff
beq no_slot_to_clear
tax
lda {sprite_size},x
beq clear_small_slot
cmp #$0001
beq clear_large_slot
txa
ldx {repeat_slot_stack_index}
sta {repeat_slot_stack},x
inx
inx
stx {repeat_slot_stack_index}
bra +

clear_small_slot:
txa
ldx {small_slot_stack_index}
sta {small_slot_stack},x
inx
inx
stx {small_slot_stack_index}
bra +

clear_large_slot:
txa
ldx {large_slot_stack_index}
sta {large_slot_stack},x
inx
inx
stx {large_slot_stack_index}
+;
tax
lda {sprite_name},x
jmp -
[/size]
Last edited by psycopathicteen on Sun Aug 06, 2017 4:22 am, edited 1 time in total.

syboxez
Posts: 32
Joined: Tue Mar 01, 2016 8:22 pm

Re: Why no SNES homebrew scene?

Post by syboxez » Sat Aug 05, 2017 7:33 pm

psycopathicteen wrote:If I'm trying to make a code library for others to use, how much shorter do I need to make this code to not look intimidating?
Comment as much as possible. There is no such thing as "too obvious" when it comes to beginners.

Revenant
Posts: 442
Joined: Sat Apr 25, 2015 1:47 pm
Location: FL

Re: Why no SNES homebrew scene?

Post by Revenant » Sat Aug 05, 2017 8:54 pm

If you have almost 350 lines of assembly code with literally zero comments, the length isn't what people are going to find intimidating about it.

psycopathicteen
Posts: 2979
Joined: Wed May 19, 2010 6:12 pm

Re: Why no SNES homebrew scene?

Post by psycopathicteen » Sat Aug 05, 2017 9:22 pm

I began trying to put comments on my code, but then realized that I should really make diagrams to visualize it too.

Let's see if I can sum this up. It uses linked VRAM slots. Each onscreen metasprite has a chain of VRAM slots with a beginning and end. Each VRAM slot has a metasprite-relative x/y coordinate and attribute tied to it. Several objects can share the same chain at once with a different origin and flip settings. There are special slot numbers that aren't real VRAM locations, but signify if a CHR number is a repeat of the last one, but with a different relative x/y or attributes, which can be useful for making tiled moving platforms.

Is that explanation good?

creaothceann
Posts: 280
Joined: Mon Jan 23, 2006 7:47 am
Location: Germany
Contact:

Re: Why no SNES homebrew scene?

Post by creaothceann » Sun Aug 06, 2017 3:36 am

psycopathicteen wrote:I began trying to put comments on my code, but then realized that I should really make diagrams to visualize it too.

Let's see if I can sum this up. It uses linked VRAM slots. Each onscreen metasprite has a chain of VRAM slots with a beginning and end. Each VRAM slot has a metasprite-relative x/y coordinate and attribute tied to it. Several objects can share the same chain at once with a different origin and flip settings. There are special slot numbers that aren't real VRAM locations, but signify if a CHR number is a repeat of the last one, but with a different relative x/y or attributes, which can be useful for making tiled moving platforms.

Is that explanation good?
Mention anything that you'd want to see if you were a user of this code.
- author (name, email, website, accounts on the websites you frequent), version, date, license
- does it require specific assembler(s), hardware (e.g. no SNES MINI) or emulators?
- operating principle
- detailed reference
- example(s)
- links
My current setup:
Super Famicom ("2/1/3" SNS-CPU-GPM-02) → SCART → OSSC → StarTech USB3HDCAP → AmaRecTV 3.10

User avatar
HihiDanni
Posts: 186
Joined: Tue Apr 05, 2016 5:25 pm

Re: Why no SNES homebrew scene?

Post by HihiDanni » Sun Aug 06, 2017 7:53 am

A few issues I am noticing with the posted code:

- This looks like one large function that is doing a lot. Separation of concerns is a key principle here. Of course for performance reasons it may be good to bundle many actions together, but if this is only getting called for maybe 16 objects a frame, you can afford to split it up.
- I can't tell which labels are intended to be called from user code, and which ones are internal branch/jump destinations. It doesn't help that the first label is simply titled "animation". Generally you'll want to name your functions after verbs, and also include the scope or target of your action in the name. Is the function processing animation for all objects? Or the current object? You may also want to include notes on when/where it is appropriate to call this function, along with input parameters and side-effects.
- Use of magic numbers ("if "animation_update" is 2, then frame is rotated 180 degrees") You should replace 2 with a constant.
- Maybe personal preference, but it could use more blank lines between groups of instructions so each "step" of the routine is more apparent. Maybe also comment overall what each step (group of instructions) is doing on a high level. Learning code is easier when you understand the general purpose of each segment of code. And personally I would find this more helpful than commenting individual lines (though if an individual line is doing something weird, might want to comment that too).

I feel like this code is trying to do a lot in general, and perhaps I'm paranoid, but it could use some optimization and redesign. That's not necessarily a bad thing - I've reworked my code several times in the past. It's rare to come up with the perfect design on the first try, and this is just the reality of software development. But splitting things up as I mentioned above, and documenting how the different systems work together (what each function expects, the format of the underlying data, etc.) will make that much easier.

Of course, you can still provide the code like it is, and then improve on it later with a new version. User testing may be some of the most valuable testing you can find. Just remember to attach a license to your code so that people can use it.
SNES NTSC 2/1/3 1CHIP | serial number UN318588627

psycopathicteen
Posts: 2979
Joined: Wed May 19, 2010 6:12 pm

Re: Why no SNES homebrew scene?

Post by psycopathicteen » Sun Aug 06, 2017 8:24 am

The thing is that it has to decode the meta sprite format in order to find the ROM address of the needed sprite graphics.

Post Reply