SNES Hi/Fast ROM Header Problems

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.
User avatar
LycanLambda
Posts: 3
Joined: Tue Aug 06, 2013 6:04 pm
Location: City 17

SNES Hi/Fast ROM Header Problems

Post by LycanLambda »

Hello everyone, I decided to start things ahead with taking advantage of the speed boost and extra space provided with Hi/FastROM. Instead of starting with LoRom. I've cobbled together my 'standard' header I'd use for all my practice programs and full projects, however I'm having a hard time with it working.

So far in higan/bsnes it gives me garbled nonsense instead of the greenscreen that I desired:
Image

And ZSNES works fine, however I receive a "Bad ROM / Checksum Error":
Image

Any idea whats wrong with my header (link: http://s000.tinyupload.com/index.php?fi ... 4610377147)
(Code here: http://s000.tinyupload.com/index.php?fi ... 9752123747)

Or have a solid fast/hirom header they'd like to share? I'm reasonably knowledgeable with the SNES's memory map and its mirrors, however WLA DX has me a bit confused.

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

Re: SNES Hi/Fast ROM Header Problems

Post by tepples »

Checksum error is normal in a freshly linked ROM. You'll probably have to correct the checksum using a separate tool. If you've been using a separate tool, and your ROM is using HiROM, you'll have to configure it to work with a header stored at a raw offset of $FFxx, not $7Fxx. But in practice, the only thing that really cares about the header checksum in a Super NES ROM is Nintendo's cart manufacturing, to make sure that the ROM reached Nintendo's office intact, and that hasn't been in operation for over a decade.

And I don't see how whether the ROM is mapped as HiROM (21) or LoROM (20) is in any way connected to whether something uses slow or fast ROM. I was under the impression that $808000-$80FFFF, $818000-$81FFFF, ..., $BF8000-$BFFFFF could still be set to fast.
User avatar
bazz
Posts: 476
Joined: Fri Sep 02, 2011 8:34 pm
Contact:

Re: SNES Hi/Fast ROM Header Problems

Post by bazz »

Please upload your SNES_init.asm file. I need to see it.
User avatar
bazz
Posts: 476
Joined: Fri Sep 02, 2011 8:34 pm
Contact:

Re: SNES Hi/Fast ROM Header Problems

Post by bazz »

without seeing your init routine, and i need to say I am not well versed in FAST ROM. So please someone verify what I say here:

1) you need to write 0 to $420D
2) At the beginning of all your vectors, you need to jump long to next instruction, ensuring that your jump changes the bank byte to the upper mirror. This can easily be done in WLA DX by setting the .BASE directive to your upper base bank desired for long jump instructions to follow. Then you want to push the program bank register and pull it into the data bank register. Now your program and data banks will be reflect fast ROM region.

So, the reason why I wanted to see your init file, was to see if you bothered doing either of these things.

http://wiki.superfamicom.org/snes/show/ ... th+FastROM
User avatar
bazz
Posts: 476
Joined: Fri Sep 02, 2011 8:34 pm
Contact:

Re: SNES Hi/Fast ROM Header Problems

Post by bazz »

In your main code file. You do not stz $2121 before writing to $2122. Unless you positively know your init routine has done this priorly, you must write the color index first into $2121. Maybe this will help you?
User avatar
koitsu
Posts: 4203
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: SNES Hi/Fast ROM Header Problems

Post by koitsu »

What Tepples said is correct; root cause of your issue is unrelated to the "failed checksum" message, and fixing that is up to a utility that runs either post-assembly or has to be handled properly by the assembler itself.

I'm of the same opinion as bazz at this point, but will expand:

1. $2121 not being set immediately prior to tinkering with $2122 is probably not wise.

2. You're not doing anything with $2115 (VRAM access mode/model), $2116/2117 (VRAM address) nor $2118/2119 (VRAM data) -- meaning, you aren't writing anything to VRAM. You're making a blind (and very bad!) assumption that the content of VRAM is is pre-zeroed/empty. This would vary from emulator to emulator. If higan/bsnes sticks "random crap" there on power-on, then good, because there's no guarantee what VRAM contains on power-on or reset on the actual console! It's your responsibility to zero this out (commonly done via DMA) as you see fit.

3. Likewise/related to 2, you're not doing anything with $2105 (selecting BG mode/size), $2107 through $210a (BG-SC size/base address), nor $210b/210c (name base address for CHR data). You've probably got $2105 as $00 (mode 0, tile size of 8x8), and $2107 (BG1) is probably pointing to $0000 in VRAM, and $210b (BG1 and BG2) is probably also pointing to $0000 in VRAM, which is probably not what you want (CHR data at the same location as the layout data? Heh ;-) Well I guess for a "blank/zero everything" you might!).

See SNES developers manual Chapter 3 (page 2-3-2 onward) for what you should be doing before turning on the screen in this case.

4. You probably aren't initialising the 65816 correctly but since you aren't providing full source code I can't tell. The very first thing you should be doing (meaning this is the code that should get pointed to by the RESET vector) is this:

Code: Select all

	.org $xx8000	; Or however this is done with WLA DX -- see below for why I say $xx, but $xx8000 is important!
RESET:			; Point RESET vector here
	sei
	clc
	xce
	jml RESETNEXT
RESETNEXT:
	jsl SNES_Init
My guess is that (2) and (3) are the cause of your problem.

(4) is also immensely important. There are multiple things going on there, so I'll itemise them:

4a) You need to make sure the assembler is operating with the knowledge that it's in either bank $c0 (normal speed mode 21 (hirom) -- probably what you want), or $80 (fast speed mode 21 but be aware of the memory layout) -- see SNES developers manual page 2-21-4 for the memory layout and see the notes at the bottom of that page -- and this also relates to (4d) below,

4b) Disabling interrupts ASAP is important, you don't want a spurious NMI or hardware IRQ happening at this point. This is just common practise as well, even on the 6502/65c02,

4c) You absolutely need to clear emulation mode on startup. The 65816 starts up in 65c02 emulation mode (e.g. e=1),

4d) The jml {next line} looks pointless but isn't. This is a "65816 quirk" per se; what's being ensured here is that the K register (sometimes called "PBR" -- "Program Bank Register") gets set to where your code should actually be running out of and not bank $00. All the 65816 vectors are bankless, i.e. 16-bit values, and bank $00 is assumed. But also remember that this same quirk/issue needs to apply to other vectors (ex. NMI).

Reworded: K is $00 on power-on, so your startup code is actually going to be executed in bank $00. If you look at a memory map for mode 21, look at what's in bank $00 -- specifically how $8000 to $ffff are mirrored from bank $c08000 to $c0ffff. Same goes for $808000 to $80ffff. This is one of the reasons why that mirroring is important, and likewise, why that jml is important -- to get out of bank $00 and into $c0 or $80 (depends on what you want). But you also need to keep the routines within the $xx8000 to $xxffff range (well $ffe3 I guess), meaning don't go doing something like .org $800000 for your RESET vector to be at $0000 (look at what's in bank $00 at $0000 ;-) ).

See SNES developers manual pages 2-24-3 and 2-24-4 for details on (4b) through (4d); they're discussed fully there. Otherwise please read (do not skim) the Western Design Center's 65816 Programmer's Manual, page 55 ("Interrupts").

You may also want or need to do a phk/plb once you've switched over to a different bank; this sets the B register to whatever K is at the time. Again you need to know what you're doing, have familiarity with your assembler, and understand the memory map fully to know what's "safe". Example: you don't want to be in bank $00 and do something like a jmp $2100, you'd want to be in bank $c0 (not $00 or $80). ;-)

Also, your SNES init routine should be this (if it isn't already) -- this is intended to be used via jsl SNES_Init (not jsr, but you can change over to jsr/rts if you want), and not as a macro. When I say "SNES init routine" I'm referring to the memory mapped registers themselves, not part of the stuff discussed above in (4).

Code: Select all

SNES_Init:
	sep #$20
	lda #$80
	sta $2100
	stz $2101
	stz $2102
	stz $2103
	stz $2104
	stz $2105
	stz $2106
	stz $2107
	stz $2108
	stz $2109
	stz $210a
	stz $210b
	stz $210c
	stz $210d
	stz $210d
	stz $210e
	stz $210e
	stz $210f
	stz $210f
	stz $2110
	stz $2110
	stz $2111
	stz $2111
	stz $2112
	stz $2112
	stz $2113
	stz $2113
	stz $2114
	stz $2114
	lda #$80
	sta $2115
	stz $2116
	stz $2117
	stz $211a
	stz $211b
	lda #$01
	sta $211b
	stz $211c
	stz $211c
	stz $211d
	stz $211d
	stz $211e
	lda #$01
	sta $211e
	stz $211f
	stz $211f
	stz $2120
	stz $2120
	stz $2121
	stz $2123
	stz $2124
	stz $2125
	stz $2126
	stz $2127
	stz $2128
	stz $2129
	stz $212a
	stz $212b
	stz $212c
	stz $212d
	stz $212e
	stz $212f
	stz $4200
	lda #$ff
	sta $4201
	stz $4202
	stz $4203
	stz $4204
	stz $4205
	stz $4206
	stz $4207
	stz $4208
	stz $4209
	stz $420a
	stz $420b
	stz $420c
	stz $420d
	rtl
This is covered in the official SNES developers manual, section 2-26-1. And yes, there is some redundant code in there (such as the unnecessary second lda #$80 and the secondary lda #$01), but that's irrelevant to your issue as well. Stay focused. :D

It would really help if you would provide the full source to everything and not just hobbled together bits. This is incredibly important when starting out.

P.S. -- Where should I send a bill for my time? ;-) Just kidding...
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: SNES Hi/Fast ROM Header Problems

Post by tepples »

Is there a reason that the recommended init code uses 8-bit stz and not the shorter 16-bit stz?
mic_
Posts: 922
Joined: Thu Oct 05, 2006 6:29 am

Re: SNES Hi/Fast ROM Header Problems

Post by mic_ »

Probably because we're talking about on the order of 100 bytes of ROM space. So why bother?
3gengames
Formerly 65024U
Posts: 2281
Joined: Sat Mar 27, 2010 12:57 pm

Re: SNES Hi/Fast ROM Header Problems

Post by 3gengames »

mic_ wrote:Probably because we're talking about on the order of 100 bytes of ROM space. So why bother?
Yeah, SNES is a modern system, we have to get used to modern programming practices. :P
mic_
Posts: 922
Joined: Thu Oct 05, 2006 6:29 am

Re: SNES Hi/Fast ROM Header Problems

Post by mic_ »

Even the earliest SNES games were 4 Mbit. So you'd be looking at savings of about 0.02% of the total ROM space even for the smallest carts. Unless you really need that extra 0.02% I see no point in optimizing the init routine.
User avatar
koitsu
Posts: 4203
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: SNES Hi/Fast ROM Header Problems

Post by koitsu »

tepples wrote:Is there a reason that the recommended init code uses 8-bit stz and not the shorter 16-bit stz?
Remember at the bottom of my post where I said "Stay focused. :D" ? ;-) It's remarkable how completely off-track threads on this forum get. What mic_ said above applies.
User avatar
LycanLambda
Posts: 3
Joined: Tue Aug 06, 2013 6:04 pm
Location: City 17

Re: SNES Hi/Fast ROM Header Problems

Post by LycanLambda »

I'm back, I've been trying to piece together how to organize the code specifically in the INC file. I do believe I've setup the banks right when it came to the initial memory map, however I'm still confused on the usage of the .BASE directive. I realize that for mode 21 hi/fastrom you can start from either $80:8000 for the PROM or from $C0:0000 however upon setting that in my INC file, I continue to receive black screens and bad rom from my emulators.

If all else fails, I'd assume moving all Program memory to start from $C0:0000 and above considering its a contiguous block of memory.

I've also cleared all the registers (I think I forgot to set $420D for the speed, but that's a lesser problem atm)

I'm also curious if I should use .ORG $C00000 at the beginning of my Reset vector and for any subroutines I might add in the future. Especially if those subroutines are in different banks.

I apologize for my ignorance on the subject, its rather new to me in practice. I come from a background in microcontroller programming where I saw my memory maps as single continuous (1 Dimensional) systems, 2-D memory maps like this and (Oh God, Pages on Windows) are still another ball game for me entirely.


All in all rip it apart and tell me whats wrong, thanks a ton for your patience and help! :mrgreen:
Attachments
Lesson2_WIP_HIROM.zip
(3.27 KiB) Downloaded 128 times
User avatar
Ramsis
Posts: 341
Joined: Sun Jul 01, 2012 6:44 am
Location: Lion's den :3
Contact:

Re: SNES Hi/Fast ROM Header Problems

Post by Ramsis »

WLA DX has a feature that calculates your ROM checksum automagically. ;) Let me rephrase your header file to take advantage of it. :)

(Note that I disabled the .BASE directive as I don't think it's actually needed. Also, I made some other corrections, like the HEADER_OFF define. BTW, all your .ORG directives in the cart header and interrupt vector sections were still lorom-based, and thus wrong. :P Oh, and RTI already implies PLP, so that's not needed in your VBlank routine either ...)

Code: Select all

;Standard HiRom Header
;2013 Nick Winston
;Standardized header for all programs thus allowing faster ROM access and full size access (Edit/Revision A)
;
;Edited from Neviksti & Bazz's LoROM Header on SNES Dev

;First setup standard memory map, slot allocation, & sys architecture
.MEMORYMAP
    SLOTSIZE $10000     ;Slot size of $10000 bytes
    DEFAULTSLOT 0       ;Default Slot for Reset, NMI and other interrupt vectors
    SLOT 0 $0000        ;Define Slot 0's starting address at $0000
.ENDME

.ROMBANKSIZE $10000     ;Every ROM Bank is 64KB
.ROMBANKS 16            ;Allocate 16 rom banks, resulting in an 8Mb cart

;.BASE $C0               ;Set PBR & DBR to $C0 to identify the upper mirror of the rom
                        ;That way the full ROM image can be accessed.
                        ;This is crucial when it comes to addressing registers, etc
                        ;
                        ;When doing long jumps to labels, this is crucial to specify the bank to
                        ;jump to. Thus jumping to labels will assure that the jump is made to the program rom

.DEFINE HEADER_OFF $0000 ; Offset Header for cartridge




.EMPTYFILL $FF          ;Obligatory fill of the rest of the bank/slot

.SNESHEADER					; this also calculates ROM checksum & complement
	ID		"SNES"
	NAME		"Lesson 2             "
	HIROM
	FASTROM
	CARTRIDGETYPE	$00
	ROMSIZE		$0A
	SRAMSIZE	$00
	COUNTRY		$01
	LICENSEECODE	$00
	VERSION		$00
.ENDSNES


.BANK 0 SLOT 0
.ORG $FFB0 + HEADER_OFF

	.DB		"00"			; new licensee code ($00 = unlicensed, not relevant unless you change the old code above to $33)



.SNESNATIVEVECTOR
	COP		EmptyHandler
	BRK		EmptyHandler
	ABORT		EmptyHandler
	NMI		VBlank
	UNUSED		$0000
	IRQ		EmptyHandler
.ENDNATIVEVECTOR



.SNESEMUVECTOR
	COP		EmptyHandler
	UNUSED		$0000
	ABORT		EmptyHandler
	NMI		EmptyHandler
	RESET		Boot
	IRQBRK		EmptyHandler
.ENDEMUVECTOR



.BANK 0 SLOT 0      ; Defines the ROM bank and the slot it is inserted in memory.
.ORG HEADER_OFF
.SECTION "EmptyVectors" SEMIFREE

EmptyHandler:
    rti
    
VBlank:
    jml FastVBlank
FastVBlank:
    rep #30     ;
    pha         ;Push Accumulator onto stack, you should also push X and Y (16 bit), and pull them back just before RTI, in case you plan to use them during (a more complex) VBlank later on!
;    php               ; not needed
    
    sep #$20
	lda $4210		;clear NMI Flag

;	plp                      ; not needed either (unless this is how you make sure to pull back 16 bit registers, of course)
    rep #$30     ; make A/X/Y 16 bits (not needed if you keep your PHP/PLP combo above, which I'd not recommend)
	pla
	rti
    

    
.ENDS
EDIT: One more thing: Your SNES_Init routine ends with RTI, but it actually has to end with RTL since it's called with a JSL. :!:

Let me know if it works (I might have missed more errors, or even created new ones ...)! :)
Some of my projects:
Furry RPG!
Unofficial SNES PowerPak firmware
(See my GitHub profile for more)
User avatar
bazz
Posts: 476
Joined: Fri Sep 02, 2011 8:34 pm
Contact:

Re: SNES Hi/Fast ROM Header Problems

Post by bazz »

It's a fun little challenge, I'll let the other guys help you out
User avatar
bazz
Posts: 476
Joined: Fri Sep 02, 2011 8:34 pm
Contact:

Re: SNES Hi/Fast ROM Header Problems

Post by bazz »

I seem to have gotten to work. This is running in bsnes, debugger shows code fetch from Fastrom Banks.

Image

All I did was 2 simple changes. in your Lesson2.asm, I just set .org to $008000. the JML makes the bank change not the .org (afaik) I'm confident.
Then I added this:

Code: Select all

Boot:
    sei ;Disable interrupts
    clc
    xce				; Note. Very important to go native / sei before JML. Crash otherwise (tried it)
    rep #$10
    sep #$20
    lda #$8f
    sta $2100		; fblank this right at Reset
    jml Start			
	
Start:
	phk
	plb
;...
JSL SNES_init. not JML.. like the guy above me said.


NOW THE IMPORTANT PART: if you want access to the hardware registers $2000-$4000, then you need to push bank $80 onto the bank register not $c0. because $c0 is ROM only. but banks $80 and up act like $00 and up where they still have access to the hardware registers ;)
Last edited by bazz on Sat Aug 17, 2013 2:34 am, edited 2 times in total.
Post Reply