Developing Satellaview games with PVSnesLib


This is a demo to showcase the way I integrate Satellaview features to a PVSnesLib project.

Video of the demo: https://youtu.be/HI_O6bwIuIU

Download the rom (bs-demo-mpack.bs): https://github.com/Krokodyl/bsdemo-mpack


First, you need to have the PVSnesLib environment set up. Follow the instructions on the wiki and try to compile any of the examples provided to make sure it’s working.

Second, you can get the sources for the demo by cloning or downloading the github repository below.

I will only go into details about the specific Satellaview features. For anything that’s general to snes dev, I’ll leave useful links at the bottom of this page.


Sources

https://github.com/Krokodyl/bsdemo-mpack


Changes


Other than the main code in .c files, I added/modified the following files:


_ file use
Added bs-features.asm bs-x API
Modified data.asm header
Modified hdr.asm new interrupt vectors
Modified Makefile integrates the converter
Added src/bstime.h src/bstime.c handles the time channel
Added tools/sfc-bs-converter-1.0.exe converts SFC into BS

bs-features.asm
bsStart
bsVBlank
bsSaveTownRam
bsReturnToTown
bsRebootBios
bsTimeReset
bsTimeUpdate
spcReset

Features


1. Reboot BIOS


rebootpng.png

Effect: Exit the game and return to the welcome screen.

Simply call spcReset() then bsRebootBios() in your code.
spcReset() puts the APU back in its initial state.
If you haven’t loaded music/sfx to the APU, you don’t have to call spcReset().
If you’re using a different sound driver than snesmod (the default in pvsneslib), you’ll have to call whatever reset APU function is available to you (or modify spcReset in bs-features.asm).
bsRebootBios() calls the reboot hook $105C3C.

Example in the demo: menu.c

if (selectedOption==OPT_REBOOT) {
    playSound(SFX_MENU_SELECT);
    spcFadeModuleVolume(0, 30);
    setFadeEffect(FADE_OUT);
    spcReset();
    bsRebootBios();
}

2. Return to town


return.png

Effect: Exit the game and return to the building from which the game was launched.

This feature is more complex than a bios reboot.

First, modify the vectors in hdr.asm


before after
NMI VBlank NMI bsVBlank
RESET tcc__start RESET bsStart

Second, call spcReset() then bsReturnToTown() in your code. (Any comment about spcReset, in the Reboot BIOS section, applies here as well)

Explanation:
bsStart will save the first $2000 bytes of WRAM and the Stack Pointer value at the address TOWN_ADDR and TOWN_ADDR-2 respectively ($7FE000 by default).
This is called as the reset vector because it needs to happen before any change is made to the wram or the stack.
You can change that address in bs-features.asm if you need to:
.EQU TOWN_ADDR $7FE000
Just make sure to leave room for $2000 bytes after and 2 bytes before. Your code should obviously not write in that area.

bsReturnToTown restores the saved WRAM and jumps back to the bs-x NMI.

bsVBlank replaces the regular VBlank as there are a few clock cycles when between the WRAM restoration and the jump to the bs-x NMI where an interrupt could occur and trigger the PVSnesLib NMI (which would break as the Stack Pointer is no longer the same).

Example in the demo: menu.c

if (selectedOption==OPT_EXIT) {
    playSound(SFX_MENU_SELECT);
    spcFadeModuleVolume(0, 30);
    setFadeEffect(FADE_OUT);
    spcReset();
    bsReturnToTown();
}

3. Time Channel


time.gif

Effect: Get the current date and time from the Time Channel.


src/bstime.h _
img.png defines a struct that will hold the time data

src/bstime.c _
img_1.png initializes the bstime struct

During the initialization of the game, you need to call initBSTime() and bsTimeReset(). bsTimeReset() resets the BS-X Stream 2 data queue and tune in channel 0 (on reg $218E).

Then everytime you want to update the bstime struct, you call bsTimeUpdate(&bstime). In the demo, I call it once per frame but you could have a counter and call it every 60 frames to the same effect.
Possible improvement: check that the values fall within the correct range.

Then it’s up to you to use the time data as you need (in the demo, I print it on background 3) with bstime.hrs, bstime.min and so on.

Example in the demo: bs-demo.c

if (showTime) {
    bsTimeUpdate(&bstime);
    printDate();
    printTime();
}

SFC to BS conversion


I made a small CLI tool, integrated to the build, in order to convert the .sfc file into a .bs file.

The tool updates the header and recalculates the checksum.

Link to the sources: https://github.com/Krokodyl/sfc-bs-converter


Integration to PVSnesLib


First, place the executable (.exe or .jar) in a tools folder at the root of your project.

Second, modify the Makefile

export SFCBSCONV := tools/sfc-bs-converter-1.0
# OR
# export SFCBSCONV := java -jar tools/sfc-bs-converter-1.0.jar

AUDIOFILES := snd/sfx.it snd/music.it
export SOUNDBANK := snd/soundbank

include ${PVSNESLIB_HOME}/devkitsnes/snes_rules

.PHONY: bitmaps all

#---------------------------------------------------------------------------------
# ROMNAME is used in snes_rules file

export ROMNAME := bs-demo-mpack

SMCONVFLAGS	:= -s -o $(SOUNDBANK) -V -b 5
musics: $(SOUNDBANK).obj

all: musics bitmaps $(ROMNAME).sfc sfcbsconv

clean: cleanBuildRes cleanRom cleanGfx cleanAudio cleanBS

cleanBS:
@echo clean BS rom
@rm -f $(ROMNAME).bs

# -d 28 -m 12	Broadcast date 12/28
# -b 29 80		Skip St.GIGA intro (0x80)
sfcbsconv:
@echo Converting rom for the Satellaview
$(SFCBSCONV) -d 28 -m 12 -b 29 80 -t "BS-DEMO-MPACK" -i $(ROMNAME).sfc -o $(ROMNAME).bs $<````

You can change SFCBSCONV to use the .exe or the jar.
You can also add options in the goal sfcbsconv. In the demo, for example, I change the broadcast date to (12/28) and the title to “BS-DEMO-MPACK”.
All available options are detailed on the tool repository.

Once you have your .bs rom, you can run it in an emulator with Satellaview support.


Useful links


PVSnesLib

Wiki

API Documentation

Sources


BS-X

No$ Satellaview Specifications

BS-X SRAM Map

BS-X MMIO / MCC Registers

Satellaview Registers

BIOS/ROM map

BS-X Satellaview Header


SNES


Retro Game Mechanics Explained (Youtube)

Super Nintendo Entertainment System Features

SPC700 Series


SNES dev

Backgrounds priority

MMIO registers

SPC700/IPL ROM

Guide to Mask Windows on the SNES

Qwertie’s SNES Documentation

Meow’s Snes Graphic Guide

Palettes

65816 ASSEMBLY

SNES memory map

65c816 reference

65816 Opcodes

65816 programming

Jay’s ASM Tutorial

Assembly for the SNES

Meow’s Sprite Engine Design Guidelines

Checksum calculation

No$ and Byuu

MSU1 A/V synchronization

Layers / Priority

Backgrounds

Transferring Data to the SNES APU

SPC700 reference


SNESMOD

Snesmod composition rules


MISC

Shift-JIS Kanji Table

Encoding converter (useful for Shift-JIS)

The Mod Archive (chiptune music)

Rilden Quantization tool

Mesen docs


Written By

Krokodyl

Comments