Awesome find! This quality scan together with the discovery of the checkered ball in action (a few month back) are to live for!
As for the parallax scrolling techniques often talked about in this demo:
I did found a few references in Sonic 1's code labels that state there were once two layers of objects in the game. Just imagine it as two cameras moving at different speeds and creating and potentially destroying objects as they move over a grid of 128 pixels. By having two object layouts, it is possible to spawn background or foreground objects at a different scroll rate, such as the rocks and clouds in the foreground. A huge amount of RAM was allocated for this effect, and it was probably removed in favor of other effects. Having foreground and background objects in the game is still possible, but require special programming as can be seen by the trusses in Starlight Zone and Chemical Plant Zone, and the Death Egg background sprite in Lava Reef Zone Act 2, and then we have special foreground palm leaves in Angel Island.
Here are some of the Sonic 1 references (ported to C). The name of the second object layer was officially Z:
extern FIX16 scra_h_posiw; // $FFF700: Scroll A horizontal.
extern FIX16 scra_v_posiw; // $FFF704: Scroll A vertical.
extern FIX16 scrb_h_posiw; // $FFF708: Scroll B horizontal.
extern FIX16 scrb_v_posiw; // $FFF70C: Scroll B vertical.
extern FIX16 scrz_h_posiw; // $FFF710: Scroll Z horizontal. << Object layer 2 scroll
extern FIX16 scrz_v_posiw; // $FFF714: Scroll Z vertical. << Object layer 2 scroll
extern FIX16 scrc_h_posiw; // $FFF718: Scroll C horizontal.
extern FIX16 scrc_v_posiw; // $FFF71C: Scroll C vertical.
extern unsigned short int scrflaga; // $FFF754: Scroll A update flags.
extern unsigned short int scrflagb; // $FFF756: Scroll B update flags.
extern unsigned short int scrflagz; // $FFF758: Scroll Z update flags. << Object layer 2 scroll
extern unsigned short int scrflagc; // $FFF75A: Scroll C update flags.
extern ASETDATA const* asetadr; // $FFF770: Actset high address.
extern ASETDATA const* asetadr2; // $FFF774: Actset low address.
extern ASETDATA const* asetadrz; // $FFF770: Actset high address for scroll Z. << Object layer 2 scroll
extern ASETDATA const* asetadrz2; // $FFF774: Actset low address for scroll Z. << Object layer 2 scroll
extern signed short int asetxposiz; // $FFF78A: Last horizontal spawn meta grid for scroll Z. << Object layer 2 scroll
extern signed short int asetyposiz; // Last vertical spawn meta grid for scroll Z. << Object layer 2 scroll
extern signed short int flagxposiz; // Current horizontal spawn meta grid for scroll Z. << Object layer 2 scroll
extern signed short int flagyposiz; // Current vertical spawn meta grid for scroll Z. << Object layer 2 scroll
extern signed short int flagxposi; // Current horizontal spawn meta grid ($FFEF64 in Sonic 3).
extern signed short int flagyposi; // Current vertical spawn meta grid ($FFF7DA in Sonic 3).
extern FLAGWKCNT flagwkcnt; // $FFFC00: Spawn remember buffer index.
extern FLAGWKCNT flagwkcntz; // $FFF78C: Spawn remember buffer index for scroll Z. << Object layer 2 scroll
In the source file ACTSET, the spawn tables are initialized according to the active stage, and some of the Z scrolling stuff is indeed left initialized in Sonic 1:
void actsetchk ()
{
unsigned int I;
signed int x, y, xz, yz;
enum
{
actsetinit,
actset
};
switch (actset_rno)
{
case actsetinit:
++actset_rno;
asetadr = asetadr2 = asettbl [(((stageno.zone << 1) + stageno.zone) + stageno.act) << 1];
asetadrz = asetadrz2 = asettbl [((((stageno.zone << 1) + stageno.zone) + stageno.act) << 1) + 1]; // << Object layer 2 scroll
flagwkcnt.flagworkcnt = flagwkcnt.flagworkcnt2 = 1;
flagwkcntz.flagworkcnt = flagwkcntz.flagworkcnt2 = 1; // << Object layer 2 scroll
for (I = 0; I < 0x200; I++)
{
flagwork [I] = 0x00;
}
if ((x = (signed int) scra_h_posiw.integer - STAGE_ACTSET) < 0)
{
x = 0;
}
x &= ~(STAGE_ACTSET - 1);
while (x > asetadr->xposi)
{
if ((signed char) asetadr->actno < 0)
{
++flagwkcnt.flagworkcnt;
}
++asetadr;
}
... Continues initializing spawn boundaries ...
Sorry about the technical stuff above. The main thing is, Sonic 1 will always be special and I hope more discoveries will follow, being it graphics, music, or code.
Off topic: I did discover the name of the queued/buffered compressed art in the labels as well. The name is
divdevwk. Remember this is not the name of the compression, but the name of the system for dividing the job of decoding a little bit of data per frame to have smooth loading of bosses etc.