Just time for a quick 'n' dirty fix to the sprites, and already looking a lot better!
Each Xevious sprite has 2 Neo Geo sprites allocated, because the Xevious hardware can select double-width and double-height sprites. I thought I had coded the logic to deactivate the 2nd sprite for the (most common) 1x1 tile sprite case, but it turns out a bug meant it wasn't actually being deactivated. That explains why there were so many sprites hidden behind the display masks at the bottom of the screen!
To make matters worse, I wasn't directly deactivating unused sprites, but rather setting the coordinates in the object table and letting the subsequent logic handle the deactivation... which in hindsight probably wasn't sufficient. Regardless I now explicitly deactivate sprites in these cases.
As a result, the bottom of the screen is completely devoid of any unused sprites now.
This of course means there's more headroom before the 96 sprites-per-scanline limitation causes any issues. And I still have a trick up my sleeve if I need it...
When I do find the time, I'll go through the sprite shadow register update routine more closely and make some proper optimisations, and also add support for flipping 1x2 and 2x1 sprites.
[An aside: I think I just realised why the Bacura hit-box is off!]