u8 in RAM at 0xE100 called SPRITE_CONTROL
76543210
ETFxxxxB
B: RW spritesUseRegBank (0=SpriteRegisterBank#0 (first half), 1=SpriteRegisterBank#1 (second half))
E: RW Enable Collision Detection Hardware (1=Enable, 0=Disable)
Controls what happens to the RAM located in the "collision registers" bank.
0xE000..0xE1FF contains the 1st set of collision registers (read/writable by the CPU when F=0, dynamically being updated/referenced by hardware when F=1)
0xE200..0xE3FF contains the 2nd set of collision registers (read/writable by the CPU when F=1, dynamically being updated/referenced by hardware when F=0)
0xE400..0xE7FF is always freely available to the CPU to use as generic RAM, and is untouched by the hardware.
(might be useful for Copper code, or SRAM bank-switching code)
NOTE: Even if the Collision Detection Hardware is disabled, there are a few bits of data located in the
collision ram, which the sprite hardware will still read while rendering.
Specifically, it provides per-sprite depth information.
If you're not using sprites, then you can use these registers for generic RAM.
F: RW Freeze RegisterSet 0 or 1
The frozen set of registers CANNOT by written to by the hardware. Thus, it is available to the CPU.
The unfrozen set is continually being updated by hardware as scanlines are rendered. It is not available to the CPU.
NOTE: It's important to swap this bit only after VBlank occurs, and before scanline -1 is visited
since after that window of opportunity has passed, the hardware will begin to reset the status
of all sprites' collision-bits in the unfrozen register-set. It will then build collision data
throughout scanlines 0..239, by updating various collision bits as it sees events occur.
The collision data will be considered "complete" on scanline 240. If you freeze the other buffer
at or after that point in time, then you will gain access to the new, VALID collision data.
Swapping register-sets mid-frame, will certainly produce unpredictable results, and may even
cause memory corruption within the collision register address space, if the hardware manages to
perform a read-modify-write operation as you are changing the F bit.
T: RW Team Mode (1=Enable, 0=Disable)
In team mode, sprites must be on different teams in order to be recognized as a collision.
Without team mode, all collisions count.
NOTE: The way to recieve new collision data, representing the previously rendered frame, is to...
- Disable the Collision Detection Hardware (E=0)
- Set F=0
- Write all sprite depths & teams to 0xE000..0xE1FF
- Set F=1
- Write all sprite depths & teams to 0xE200..0xE3FF
- Wait for VBlank (no valid collision data will be available for this frame unless you can get this done entirely within the VBlank)
- Enable the Collision Detection Hardware (E=1)
- Game Loop
- Wait for VBlank
- Toggle the Freeze bit (F=!F)
- move sprites before VBlank ends, in order to maximize validity of next frames' collision data,
and avoid on-screen sprite shearing. Note that you could use the sprite register double buffering
to make this far more reliable.
- if F==0: CPU can read/write the frozen collision registers from 0xE000..0xE1FF, at its' leisure.
- if F==1: CPU can read/write the frozen collision registers from 0xE200..0xE3FF, at its' leisure.
u8 in RAM at 0xE105 called SPRITE_BASE_PALETTE
76543210
xBBB0000
B: RW Base Palette Value (the bottom bits will be provided by each sprite's control register)
u8 in RAM at 0xE109 called SPRITE_CLIPRECT_CONTROL
76543210
EIxxxxAB
E: RW Enable (0=cliprect is ignored, 1=cliprect is functional)
I: RW Inverse (0=remove what's inside the box, 1=keep only what's inside the box)
A: RW X2BIT8 (The 9th (high) bit of the X2 coordinate)
B: RW X1BIT8 (The 9th (high) bit of the X1 coordinate)
u8 in RAM at 0xE10A called SPRITE_CLIPRECT_X1
76543210
VVVVVVVV
V: RW X1 (The low 8 bits of the X1 coordinate)
NOTE: The high bit is in the sprite cliprect control
u8 in RAM at 0xE10B called SPRITE_CLIPRECT_Y1
76543210
VVVVVVVV
V: RW Y1 (The Y1 coordinate)
u8 in RAM at 0xE10C called SPRITE_CLIPRECT_X2
76543210
VVVVVVVV
V: RW X2 (The low 8 bits of the X2 coordinate)
NOTE: The high bit is in the cliprect control
u8 in RAM at 0xE10D called SPRITE_CLIPRECT_Y2
76543210
VVVVVVVV
V: RW Y2 (The Y2 coordinate)
typedef struct SpriteCollisionRegisterStruct
{
union
{
struct
{
// 111111
// 5432109876543210
// IIIIIIII3210STDD
//
// I: R. Highest SpriteID that we collided with (always a number that's lower than our own ID, or 0xFF if no collision)
// 0: R. Collided with BG0
// 1: R. Collided with BG1
// 2: R. Collided with BG2
// 3: R. Collided with BG3
// S: RW Collided with Sprite
// T: RW My Team#
// D: RW My Depth (0..3)
u8 mFlags; // IIIIIIII[3210STDD]
u8 mSpriteId; // [IIIIIIII]3210STDD
};
struct
{
u16 mReg; // IIIIIIII 3210STDD
};
};
} SpriteCollisionRegister;
// Produce the 8bit value that can be written to mFlags
#define MAKESPRITECOLLFLAGS( myTeamNumber, myRenderDepth ) \
( \
( \
( \
((u8)(myTeamNumber))&1 \
) << 2 \
) | ( \
( \
((u8)(myRenderDepth))&3 \
) \
) \
)
// Produce the 16bit value that can be written to mReg
#define MAKESPRITECOLLREG( myTeamNumber, myRenderDepth ) \
(u16)MAKESPRITECOLLFLAGS( myTeamNumber, myRenderDepth )
// Some often-used values, as handy defines.
#define FOREGROUND_SPRITE_COLL MAKESPRITECOLL( 0, 3 )
#define BACKGROUND_SPRITE_COLL MAKESPRITECOLL( 0, 0 )
// (Only accessable when VIDEOBANKREG == VB_SpriteCollisionRegisters)
__AT(,,0xe000,) SpriteCollisionRegister gSpriteCollBank[2][256]; // 0xe000
(Only accessable when VIDEOBANKREG == VB_SpriteCollisionRegisters)
You can test for a collision with a sprite, by checking for FF in the ID, or by checking for the COLL_COLLIDED_WITH_SPRITE flag. The reason that both pieces of information are offered, is so that a simple bit-check can be combined with BG tests as well, if desired. Actually looking up the ID is considered to be optional additional data that the CPU may or may not be interested in.
Testing COLL_COLLIDED_WITH_SPRITE, or testing the mSpriteId for NO_SPRITE_COLLISION_ID, are both considered equally valid ways to test for a collision / no-collistion situation involving this sprite and another sprite.
typedef struct SpriteRegisterStruct
{
union
{
struct
{
// [3] [2] [1] [0]
// PPPPRRRX XXXXXXXX YYYYYYYY IIIIIIII
// P: RW Palette (0..15)
// R: RW Sprite Rotation (0..7)
// I: RW Image Index (0..255)
// X: RW X coordinate (0..511)
// Y: RW Y coordinate (0..255)
u8 mImage; // PPPPRRRX XXXXXXXX YYYYYYYY[IIIIIIII]
u8 mY; // PPPPRRRX XXXXXXXX[YYYYYYYY]IIIIIIII
u8 mX; // PPPPRRRX[XXXXXXXX]YYYYYYYY IIIIIIII
u8 mPaletteRotX; // [PPPPRRRX]XXXXXXXX YYYYYYYY IIIIIIII
};
struct
{
u32 mReg; // PPPPRRRX XXXXXXXX YYYYYYYY IIIIIIIII
};
};
} SpriteRegister;
#define MAKESPRITEREG( x, y, img, pal, rot ) \
( \
( \
( \
((u32)(pal))&0x0F \
) << 28 \
) | ( \
( \
((u32)(rot))&0x07 \
) << 25 \
) | ( \
( \
((u32)(x))&0x1FF \
) << 16 \
) | ( \
( \
((u32)(y))&0xFF \
) << 8 \
) | ( \
( \
((u32)(img))&0xFF \
) \
) \
)
#define OFFSCREEN_SPRITE MAKESPRITEREG(0,240,0,0,0)
/*
000 ... 001 ..S 010 .Y. 011 .YS
0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3
0 X X X 0 X X X X 0 X X 0 X X X X
1 X X 1 X X 1 X X X 1 X X
2 X X X 2 X X 2 X X 2 X X
3 X X 3 X X 3 X X X 3 X X
100 X.. 101 X.S 110 XY. 111 XYS
0 1 2 3 0 1 2 3 0 1 2 3 0 1 2 3
0 X X X 0 X X 0 X X 0 X X
1 X X 1 X X 1 X X X 1 X X
2 X X X 2 X X 2 X X 2 X X
3 X X 3 X X X X 3 X X X 3 X X X X
000, 011, 110, 101 (rotate a NORMAL image CW)
100, 111, 010, 001 (rotate a FLIPX'd image CW)
010, 001, 100, 111 (rotate a FLIPY'd image CW) NOTE: Same numbers as a FLIPX'd rotation
*/
// Sprite Rotation Value is 3 bits wide.
// XYS
// X: FlipX 1=FlipX, 0=Normal
// Y: FlipY 1=FlipY, 0=Normal
// S: SwapXY 1=Swap X&Y, 0=Normal
// NOTE: Flips are applied before the potential swap.
// (Only accessable when VIDEOBANKREG == VB_SpriteRegisters)
__AT(,,0xE000,) SpriteRegister gSpriteRegsBank[2][256]; // 0xE000
(Only accessable when VIDEOBANKREG == VB_SpriteRegisters)
Name | Value | Comment |
SR_Normal | 0 | |
SR_FlipX | 4 | |
SR_FlipY | 2 | |
SR_NormalRot0 | 0 | |
SR_NormalRot90 | 3 | |
SR_NormalRot180 | 6 | |
SR_NormalRot270 | 5 | |
SR_FlipXRot0 | 4 | |
SR_FlipXRot90 | 7 | |
SR_FlipXRot180 | 2 | |
SR_FlipXRot270 | 1 | |
SR_FlipYRot0 | 2 | |
SR_FlipYRot90 | 1 | |
SR_FlipYRot180 | 4 | |
SR_FlipYRot270 | 7 |