Open gitendo opened 1 year ago
Sprite 32x32 size select 0x9a00 start index of 32x32 sprites 0x9a01 end index of 32x32 sprites (exclusive) So 0,0 = no 32x32 sprites 0,8 means indexes 2 to 7 (exclusive) are 32x32 Maximum number in both is 0xf
This would have mostly been used for the title screen logo which is half sprites and half characters.
On Fri, 21 Apr 2023, 18:49 tmk, @.***> wrote:
Hello! I have small, personal project https://github.com/gitendo/bombjack where I try to document Bomb Jack hardware using game disassembly. While some things seem to be obvious I've stumbled upon $9A00 register/address. Do you happen to know what it does / how it works?
I've noticed that game zerofills $9A00-$9AC8 at $015F address. Then only writes to $9A00 during VBLANK after sprite table at $9820 is updated. Routine at $0958 sorts sprites by size. 3232 are placed at the beginning of the table and 1616 ones at the end. Upon exit $9A00 is written with number of 32*32 sprites + 3.
While sprites count makes sense the rest is mystery. I've quickly checked Senjyo and Star Force but there's no sign of $9A00 being used there that way. Couldn't google any results either, so I was hoping maybe you know the details? :)
— Reply to this email directly, view it on GitHub https://github.com/martinpiper/BombJack/issues/6, or unsubscribe https://github.com/notifications/unsubscribe-auth/AASJDQMFDWPORHXPGULGHOTXCJQ25ANCNFSM6AAAAAAXGWWXEA . You are receiving this because you are subscribed to this thread.Message ID: @.***>
Yes, I've found that info in your repository but I assumed this is some sort of extra feature you added to your project?
I don't see any access to $9A01
in Bomb Jack disassembly. This is the mentioned routine and only place where $9A00
is updated. It's called from VBLANK.
sub_958:
ld ix, unk_9878 ; vram: sprite table last entry (8 bytes)
ld iy, unk_9820 ; vram: sprite table first entry
ld hl, unk_8500 ; ram: sprite table mirror
ld b, $0C ; 12 sprites * 8 (4 byte entry followed by 4 zeroes )
ld c, 3 ; ** mystery **
loc_967:
ld a, b ; all sprites processed?
cp 0
jr z, loc_97D ; yes
inc hl ; skip 1st byte of sprite entry (tile id)
ld a, (hl) ; get 2nd byte (attribute)
dec hl ; move back to 1st byte (tile id)
bit 5, a ; it's 32*32 sprite if 5th bit is set
jr nz, loc_978
; 16*16 sprite
call sub_982 ; de = ix, copy 8 bytes from (hl) to (de), ix - 8, b - 1
jr loc_967
loc_978: ; 32*32 sprite
call sub_996 ; de = iy, copy 8 bytes from (hl) to (de), iy + 8, b - 1, c + 1
jr loc_967
loc_97D: ; sorting / update finished
ld a, c
ld ($9A00), a ; ** mystery **
ret
Why c
is loaded with 3 here, even when there's none of 32*32 sprites on the screen? I really don't get it.
This logic is from the original schematics. 7R decodes if $9a00 or $9a01 are being written to, see the area circled in red. The writes go into two separate 4-bit latches (6R and 6S) for DB[0..3] for the lo/hi sprite size registers: [image: image.png] These two 4-bit values are then used with the current sprite index (divided by 2) being rendered with two comparators (5R and 5S). The output from these two comparisons are then combined (XOR) to decide if the sprite should be 16x16 or 32x32. The final logic to use the sprite size update is done by 6T which feeds into 7S which controls the sprite load control signals, which decides what 8x8 quadrant is being used in the sprite.
Due to the way the $9axx range is decoded any write in the rane $9a00 - $9bff will be stored into the two 4-bit latches depending on if it is an odd or even address. So I suppose in the disassembly you would be looking for any write in that range $9a00 - $9bff.
If I recall correctly, the inputs to the comparators are also quite strange, the comparison seems to be setting the carry (A<B) test incorrectly. This results in slightly unexpected behaviour for when 32x32 sprites are used. Also if the lower value is higher than the higher value then the 32x32 sprite select is also quite strange.
If the high value really is never set in the disassembly, then I would have to suggest checking the real arcade hardware for any power on reset sequential memory. Or checking the real hardware default power-on state of the 4-bit latches 6R and 6S. If for example those two latches are always $f or $0 at power-on then the code might be assuming this operation.
The behaviour of the simulated hardware in my schematic design tool was so strange that I spent ages testing and mapping the various combinations of lo/hi latches and the 32x32 size output in this test: https://github.com/martinpiper/BDD6502/blob/41784363acacc702f62a81251a50457fd8dc1b9f/features/TestVideoHardware.feature#L645
I manually checked my software emulation with the observed hardware behaviour and then automated the emulation checks with expected output image data: https://github.com/martinpiper/BDD6502/blob/41784363acacc702f62a81251a50457fd8dc1b9f/features/TestVideoHardware.feature#L1238 All images with the prefix "TC-9-" are used for this test: https://github.com/martinpiper/BDD6502/tree/41784363acacc702f62a81251a50457fd8dc1b9f/testdata
For example this image shows only some sprites are 32x32: https://raw.githubusercontent.com/martinpiper/BDD6502/41784363acacc702f62a81251a50457fd8dc1b9f/testdata/TC-9-000080.bmp
On Sat, 22 Apr 2023 at 04:22, tmk @.***> wrote:
Yes, I've found that info in your repository but I assumed this is some sort of extra feature you added to your project?
I don't see any access to $9A01 in Bomb Jack disassembly. This is the mentioned routine and only place where $9A00 is updated. It's called from VBLANK.
sub_958: ld ix, unk_9878 ; vram: sprite table last entry (8 bytes) ld iy, unk_9820 ; vram: sprite table first entry ld hl, unk_8500 ; ram: sprite table mirror ld b, $0C ; 12 sprites * 8 (4 byte entry followed by 4 zeroes ) ld c, 3 ; mystery
loc_967: ld a, b ; all sprites processed? cp 0 jr z, loc_97D ; yes inc hl ; skip 1st byte of sprite entry (tile id) ld a, (hl) ; get 2nd byte (attribute) dec hl ; move back to 1st byte (tile id) bit 5, a ; it's 3232 sprite if 5th bit is set jr nz, loc_978 ; 1616 sprite call sub_982 ; de = ix, copy 8 bytes from (hl) to (de), ix - 8, b - 1 jr loc_967
loc_978: ; 32*32 sprite call sub_996 ; de = iy, copy 8 bytes from (hl) to (de), iy + 8, b - 1, c + 1 jr loc_967
loc_97D: ; sorting / update finished ld a, c ld ($9A00), a ; mystery ret
Why c is loaded with 3 here, even when there's none of 32*32 sprites on the screen? I really don't get it.
— Reply to this email directly, view it on GitHub https://github.com/martinpiper/BombJack/issues/6#issuecomment-1518297665, or unsubscribe https://github.com/notifications/unsubscribe-auth/AASJDQJMSXAQKOK7SRSAQBDXCLUAXANCNFSM6AAAAAAXGWWXEA . You are receiving this because you commented.Message ID: @.***>
-- Information in this email is strictly confidential, please do not forward its contents to any third party.
Sorry the previous attached image didn't come through, this is it:
If I recall, due to the way the 32x32 sprite size comparators are setup, a value of 3 might not render any visible 32x32 sprites. If I recall, only a value >= 4 will result in at least one 32x32 sprite being rendered and the first two sprites are never 32x32 in size.
Also remember there are only 384 maximum pixel clocks for each scanline. Usually with 16x16 sprites this means 24 (=384/16 pixels) maximum 16x16 sprites are available. However if all sprites are 32x32 then this means a maximum of 12 sprites are visible on a line. When a sprite is 32x32 then only half the sprite registers in $98xx are used.
If you have the arcade code disassembly, I would be interested to have a look at it.
Thank you for detailed explanation! Unfortunately, I don't have access to Bomb Jack PCB and honestly I'm more of software than hardware person. That's why I wanted to ask someone who has the skills I don't.
I'm pretty sure $9A01
is only zeroed after initial setup / checkerboard screen which happens at address $015F
:
ld hl,$9A00
ld bc,$C8
call sub_0850 ; zero fill (hl) to (hl+bc)
Also checked that in MAME debugger setting such watch point:
wp 9a00,1ff,w,wpaddr>9a00,{ printf "Write at %04X (%02X) PC=%04X",wpaddr,wpdata,pc ; g }
Actually this isn't a problem, since you've mentioned the sprite size comparators behavior. I understand now that value of 3 is mandatory here. This was important because I thought of programming something for this PCB. Sure, it's safe bet to copy original routine but I just prefer to know why it's done that way. :) I'm familiar with sprite limit changing due to size thanks to Gameboy. It shares a lot of similar concepts which was really helpful.
I've attached my disassembly but please beware of some cryptic comments. This is still WIP. I hope to finish it sooner or later. Final version will assemble back to ROM and serve as a base for further work. bombjack.zip
Using hardware simulation of pretty much the original schematic, I've produced an animation of all 256 combinations of the $9a00 $9a01 registers to show what sprites are affected: https://github.com/martinpiper/BombJack/blob/master/output/V2.0%20-%20Sprite%20size%20debug.gif
The test data used is: https://github.com/martinpiper/BombJack/blob/master/TestData%20-%20V2.0.txt#L218
Each line with "^-$01" waits for the next vsync. The other lines store various values into the $9a00 $9a01 registers and also the char screen memory.
Note how sprite index 12 (the 7th 32x32 sprite) is displaying strange data. I think this might be a bug in the original hardware, or unexpected data in the sprite definition memory.
Note, 32x32 sprite data can come from any area in the sprite definition memory. 32x32 sprites are not limited to the upper block of sprite memory which some versions of MAME were incorrectly assuming.
PS. I changed the sprite frames to use only the 4-9 range and the 7th 32x32 sprite is now fine, so it's probably just sprite frame data being slightly unexpected:
Ahh yes, the sprites displaying the vertical lines pattern are from the "Start!" sprites (the screen is rotated)
I changed the sprite frames used, plus their palettes and flips, which demonstrates that the frame and palette data is only read for the start of each 32x32 sprite, it ignores any changes in the next sprite definition register.
Compared to no sprite sizes:
Yeah, 'START' sprite looks a bit weird when displayed as 32*32 but it's just fine. Anyway, this is a great find that you can use 32*32 sprites from 16*16 sprite definition memory! Didn't expect it to be possible.
Unfortunately the version of MAME I tried doesn't render 32x32 sprites correctly. During the title screen I used the debugger to set sprite0 with frame 04 and it only rendered one robot sprite instead of the full 32x32 sprite shows 2x2 robots that the original hardware displays.
Also in this version 9a00 is not mapped. It seems to be incorrectly using the sprite frame to trigger 32x32 sprite display.
Not mapped in the MAME source: https://github.com/mamedev/mame/blob/423a1c96337a1cbbbe2c594d194cb5ec9138ab15/src/mame/tecmo/bombjack.cpp#L328
And we can clearly see the wrong assumption about sprite size in the MAME core: https://github.com/mamedev/mame/blob/423a1c96337a1cbbbe2c594d194cb5ec9138ab15/src/mame/tecmo/bombjack.cpp#L257
I'm pretty sure no one really checked it the way you did. When I started to reverse engineer the code I was confused at first. I've read about the maximum of 24 sprites but the routine does only 12 and each entry is 8 bytes instead of 4. I assumed programmers decided to take a shortcut and treat all entries as 32*32. This allows to not bother with $9A01
at all. Just sort sprites so 32*32 are being first, update $9A00
and it's done. Way easier to understand after your excellent explanation with an animation of all register combinations!
As for MAME driver we have the 7th bit of 1st byte which holds sprite / tile code. This makes things convenient but also wrong as you noticed. They should have used 5th bit of 2nd byte instead, that's how game code tags 32*32 sprites. Still not perfect but much closer to hardware result. But then again, this was probably "best effort" and as they say "if it ain't broke don't fix it". No one thought we'll be digging in the code trying weird things. :)
I forgot to ask, do you think there's sprite limit per display line? For example, Gameboy has 40 sprites but when there's more than 10 in one horizontal line flickering appears and it's officially not recommended to use more.
The sprite limit is 384 pixels for each scan line divided by 16 pixels for each sprite, which gives a maximum of 24 sprites active on the same scan line. This describes the scan line schedule in detail: https://github.com/martinpiper/BombJack/blob/master/README.md#raster-line-schedule
On Tue, 25 Apr 2023 at 19:52, tmk @.***> wrote:
I'm pretty sure no one really checked it the way you did. When I started to reverse engineer the code I was confused at first. I've read about the maximum of 24 sprites but the routine does only 12 and each entry is 8 bytes instead of 4. I assumed programmers decided to take a shortcut and treat all entries as 3232. This allows to not bother with $9A01 at all. Just sort sprites so 3232 are being first, update $9A00 and it's done. Way easier to understand after your excellent explanation with an animation of all register combinations!
As for MAME driver we have the 7th bit of 1st byte which holds sprite / tile code. This makes things convenient but also wrong as you noticed. They should have used 5th bit of 2nd byte instead, that's how game code tags 32*32 sprites. Still not perfect but much closer to hardware result. But then again, this was probably "best effort" and as they say "if it ain't broke don't fix it". No one thought we'll be digging in the code trying weird things. :)
I forgot to ask, do you think there's sprite limit per display line? For example, Gameboy has 40 sprites but when there's more than 10 in one horizontal line flickering appears and it's officially not recommended to use more.
— Reply to this email directly, view it on GitHub https://github.com/martinpiper/BombJack/issues/6#issuecomment-1521657200, or unsubscribe https://github.com/notifications/unsubscribe-auth/AASJDQKJFB2KUHK34AOUUBDXC63GHANCNFSM6AAAAAAXGWWXEA . You are receiving this because you commented.Message ID: @.***>
-- Information in this email is strictly confidential, please do not forward its contents to any third party.
Also it's worth noting that by changing the sprite Y position multiple times down the screen it is possible to display the same sprite several times: https://youtu.be/vDuSqMVrGko?t=187
Awesome, so basically no limit and sprite multiplexing. But I suppose there's no fancy register telling you which raster line is being drawn, or just been drawn? Everything should be calculated manually?
Correct. I used a timer to calculate the raster.
On Tue, 25 Apr 2023, 20:27 tmk, @.***> wrote:
Awesome, so basically no limit and sprite multiplexing. But I suppose there's no fancy register telling you which raster line is being drawn, or just been drawn? Everything should be calculated manually?
— Reply to this email directly, view it on GitHub https://github.com/martinpiper/BombJack/issues/6#issuecomment-1521704570, or unsubscribe https://github.com/notifications/unsubscribe-auth/AASJDQP2CUS3HPHJWFNZ7GTXC67MXANCNFSM6AAAAAAXGWWXEA . You are receiving this because you commented.Message ID: @.***>
Hello! I have small, personal project where I try to document Bomb Jack hardware using game disassembly. While some things seem to be obvious I've stumbled upon
$9A00
register/address. Do you happen to know what it does / how it works?I've noticed that game zerofills
$9A00-$9AC8
at$015F
address. Then only writes to$9A00
during VBLANK after sprite table at$9820
is updated. Routine at$0958
sorts sprites by size. 32*32 are placed at the beginning of the table and 16*16 ones at the end. Upon exit$9A00
is written with number of 32*32 sprites + 3.While sprites count makes sense the rest is mystery. I've quickly checked Senjyo and Star Force but there's no sign of
$9A00
being used there that way. Couldn't google any results either, so I was hoping maybe you know the details? :)