SML2-Gegner
aus RHWiki, der freien Romhacking-Enzyklopädie
Zitat von HyperHacker (aus einer älteren Version des Acmlm Boards):
Well I poked at the code and figured out the very complicated sprite data. At $E037 is a 2-byte pointer table, indexed by level number, to the sprite data (all data is in ROM bank 3). Level number can be retrieved from RAM address $A269. (For some reason this game keeps everything in SRAM. ) The first level is 0, Tree Zone 1 is 1, that auto-scroll level near the tree is 0x19 and so on. Level 00's data can be found at $E077. Each sprite is 3 bytes. Sprite data ends when the first byte is 0xFF. This is where it gets "fun"... It's easiest if you divide the 3 bytes into groups of bits.
Level 00, first Goomba (21 3B 3E @ $E077):
ABCD EFGH-IJK LMNOP-QRS TUVWX
0010 0001-001 11011-001 11110
Byte 1 = A-H
Byte 2 = I-P
Byte 3 = Q-X
The sprite's X position in pixels, starting from the leftmost point in the level, is determined as such:
0000 EFGH LMNO P000
The sprite's Y position in pixels, starting from the highest point in the level, is determined as such:
???? ???? TUVW X000
(Not sure how it gets the high byte... this code is the work of satan. )
The sprite's type is determined through this nightmarish formula:
1: Get the first byte
2: Rotate it right twice, and clear the low 2 and high 2 bits (AND 0x3C)
3: If bit 2 is set, clear it and set bit 6
4: x = result
5: Get the second byte
6: Rotate right once
7: Swap the low and high halves (eg 1F -> F1)
8: Clear all but the low 3 bits (AND 7)
9: Add x
So to shorten that a bit, I think it would look like: (0FCD E000) + (0000 0IJK).
Also, not sure about X/Y. I think they start from the absolute highest/left-most position possible, but it might just be relative to the highest/left-most position in the level. Have fun!
Also, these RAM addresses might be useful:
AF30: Mario's X position, 16-bit, low byte first
AF32: Mario's Y position, same as x
there are several other position counters here, each with different values, but they only change when the screen scrolls. Most of them appear to have the high byte first. Changing this value doesn't move Mario...
FF97, FFF0 = frame counters; FFF0 stops when game is paused
FF80 = joypad input
AF06=Spriteset, 0-1 (images + behaviour - try 01 in Tree Zone 1!)
AF2B=Seems to indicate what a sprite is doing; changes to 0x20 when a Koopa is moving right and 0 when it's moving left
FFD5=Different values depending on current sprite being processed (01=Koopa, 02=Shell, 03=Thrown shell, 04=Held shell, 08=Paragoomba, 09=Goomba, 0A=Dead Goomba)
FFD9=Current sprite's Y position relative to top of screen
FFDA=Current sprite's X position relative to left of screen
FFDB=Current sprite type
FFDB changes to different animation frames of the current sprite... the game must swap sprite data into these addresses before processing it (hence why changing them won't affect the sprite)
AD00 = Sprite info for sprites that are on the screen
AB00-AB05 = unused (set to 0xFF @ level start)
AB06 = sprite info for entire level
At AB06 the sprite data is decompressed into a nice neat format:
Byte 1, 2: X position, 16-bit, high byte first
Byte 3: Dunno
Byte 4, 5: Y position, 16-bit, high byte first
Byte 6: Sprite type
At AD00, the sprites that are currently active are copied from AB06, and 10 additional bytes are added:
Byte 7, 8: Dunno
Byte 9: Anything besides 02 makes it disappear until you change it back, except for 01 which the game just changes to 02.
Byte 10: How many pixels it is from the left edge of the screen. If it's more than 255 pixels away it becomes inactive. Not sure how it handles sprites being past the left edge.
Byte 11: Current animation frame. Looks like it, anyway.
Byte 12, 13, 14: Colour-related. Changing to 0x10 (or 0x0F for byte 14) changes colours.
Byte 15: Which way the sprite is moving. 1=Left, 2=Right.
Also, the sprite-loading code is at 03:6BBB ($EBBB).
Finally, here's how to decode the sprite graphics:
At 03:40B1 (spriteset 0) or 03:4F11 (spriteset 1) is a 2-byte pointer table to sprite image data, indexed by image number. Image number isn't the same as sprite number. 00 is Goomba, 04 is Koopa. Take the sprite number and multiply by 2, and add to either $40B1 or $4F11 depending on spriteset. This is the pointer to the sprite's image, low byte first, ROM bank 3. Here you'll find a series of 4-byte sprite attributes. Byte 1 is Y position, byte 2 is X position, byte 3 is tile #, byte 4 is tile attributes, just like in the GB's sprite memory. The data ends when byte 1 = 0x80. I'm not sure exactly what the X/Y location are for... if you change them, the sprite's image will keep jumping around.
Goomba's data is at 03:4213:
y x t a
F0 F8 B1 00
F0 00 B1 20
F8 F8 B2 00
F8 FF B4 20
80
which means:
-tile B1, attribute 00, at X=F8, Y=F0
-tile B1, attribute 20, at X=00, Y=F0
-tile B2, attribute 00, at X=F8, Y=F8
-tile B4, attribute 20, at X=FF, Y-F8
-end of data
And here's the graphic loading code I just described, "re-written" in C:
#define SpriteSet 0xAF06
#define FreeOAMSlot 0xFF8D
#define CurrentSpriteY 0xFFD9
#define CurrentSpriteX 0xFFDA
#define CurrentSprite 0xFFDB
mem[SpriteSet] = 0;
mem[0xAF2B] = 0;
mem[FreeOAMSlot] = 0x18;
mem[CurrentSpriteY] = 0;
mem[CurrentSpriteX] = 0;
mem[CurrentSprite] = 0;
//A, B, C, D, E, H, L, BC, DE and HL are CPU registers.
//mem[] refers to GB memory.
//rl() refers to GB opcode 'rl' (rotate left).
void LoadSpriteTSA() //03:4000
{
if(mem[SpriteSet]) = 0
HL = 0x40B1;
else
HL = 0x4F11;
D = 0;
E = mem[CurrentSprite];
E = E << 1;
D = rl(D); //Will rotate the high bit of the previous operation into D
HL += DE; //HL must point to a pointer table of some sort now (0x40C7 for Goomba)
E = mem[HL];
D = mem[HL + 1]; //Because DE now points to the sprite TSA data (0x42C9)
HL = 0xA100 + mem[FreeOAMSlot]; //A100 is a mirror of OAM (FE00)
B = mem[CurrentSpriteY];
C = mem[CurrentSpriteX];
while(mem[DE] != 0x80); //DE = Tile Y position
{
if(mem[0xAF2B] & 0x40)
A = (mem[DE] ^ 0xFF) - 7;
else
A = mem[DE];
A += B;
if(A < 0xA0)
{
mem[HL] = A;
HL++;
DE++; //DE = Tile X position
if(!mem[0xAF2B] & 0x20)
A = mem[DE];
else
A = (mem[DE] ^ 0xFF) - 7;
A += C;
if(A < 0xA8)
{
mem[HL] = A;
HL++;
DE++; //DE = Tile #
mem[HL] = mem[DE];
HL++;
DE++; //DE = Attribute
A = mem[DE] ^ mem[0xAF2B]
mem[hl] = A;
mem[FreeOAMSlot] = L;
DE++; //DE = Y position (next tile)
}
else
{
L--;
mem[HL] = 0;
DE += 3; //DE = Y position (next tile)
}
}
else
DE += 4; //DE = Y position (next tile)
}
}
Mapheader | Maps | Levelgrafiken | Gegner | Overworld | Bereiche | Titelscreen | Auswahl des Savestates | Musik | Credits | Saves | Engine | Warps

