Modified k052591 PMC emulation to reflect how the real programs work (#11992)

* Modified k052591 PMC emulation to reflect how the real programs work
* spy: Confirmed projection function constants, more accurate collision check without the need for special case handling
* thunderx: Simplified collision check, fixed object flags updates
* hexion: Added special 16-byte VRAM clearing command
* Added comments to PMC program dumps
* Use multibyte.h functions, variable scope and type cleanup
This commit is contained in:
Furrtek 2024-02-03 01:00:00 +01:00 committed by GitHub
parent 534af5344a
commit bfdcc04c7d
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 325 additions and 326 deletions

View File

@ -10,71 +10,81 @@ Notes:
- The board has a 052591, which is used for protection in Thunder Cross and - The board has a 052591, which is used for protection in Thunder Cross and
S.P.Y. In this game, however, the only thing it seems to do is clear the S.P.Y. In this game, however, the only thing it seems to do is clear the
screen. screen.
This is the program for the 052591:
00: 5f 80 01 e0 08 This is the 052591 PMC code loaded at startup, it contains a RAM/VRAM filling program.
01: df 80 00 e0 0c See https://github.com/furrtek/SiliconRE/tree/master/Konami/052591 for details
02: df 90 02 e0 0c
03: df a0 03 e0 0c 00: 5f 80 01 e0 08 Entry point, set OUT0 high
04: df b0 0f e0 0c 01: df 80 00 e0 0c r0 = 0
05: df c0 ff bf 0c 02: df 90 02 e0 0c r1 = 2
06: 5c 02 00 33 0c 03: df a0 03 e0 0c r2 = 3
07: 5f 80 04 80 0c 04: df b0 0f e0 0c r3 = f
05: df c0 ff bf 0c ExtAddr = 1fff, r4 = ffff
06: 5c 02 00 33 0c
07: 5f 80 04 80 0c Write 2 to RAM (1fff) m_bankctrl, select pmcram
08: 5c 0e 00 2b 0c 08: 5c 0e 00 2b 0c
09: df 70 00 cb 08 09: df 70 00 cb 08 r7 = RAM(4)
0a: 5f 80 00 80 0c 0a: 5f 80 00 80 0c ExtAddr = 0
0b: 5c 04 00 2b 0c 0b: 5c 04 00 2b 0c
0c: df 60 00 cb 08 0c: df 60 00 cb 08 r6 = RAM(0) (commands 0, 1 and 30 are used)
0d: 5c 0c 1f e9 0c 0d: 5c 0c 1f e9 0c JP 1F if r6 == 0
0e: 4c 0c 2d e9 08 0e: 4c 0c 2d e9 08 JP 2D if r6 == 1
0f: 5f 80 03 80 0c
Command anything other than 00 or 01: Set bank to r7, then clear 16 bytes starting from r5.w
0f: 5f 80 03 80 0c ExtAddr = 3
10: 5c 04 00 2b 0c 10: 5c 04 00 2b 0c
11: 5f 00 00 cb 00 11: 5f 00 00 cb 00 Read MSB from RAM[3]
12: 5f 80 02 a0 0c 12: 5f 80 02 a0 0c ExtAddr = 2
13: df d0 00 c0 04 13: df d0 00 c0 04 r5.w = RAM[3], RAM[2]
14: 01 3a 00 f3 0a 14: 01 3a 00 f3 0a acc = r5 + r3 = r5 + f
15: 5c 08 00 b3 0c 15: 5c 08 00 b3 0c
16: 5c 0e 00 13 0c 16: 5c 0e 00 13 0c Write 3 to RAM[1fff] m_bankctrl
17: 5f 80 00 a0 0c 17: 5f 80 00 a0 0c
18: 5c 00 00 13 0c 18: 5c 00 00 13 0c Write r7 to RAM[0]
19: 5c 08 00 b3 0c 19: 5c 08 00 b3 0c
1a: 5c 00 00 13 0c 1a: 5c 00 00 13 0c Write 0 to RAM[1fff] m_bankctrl, select vram
1b: 84 5a 00 b3 0c 1b: 84 5a 00 b3 0c
1c: 48 0a 5b d1 0c 1c: 48 0a 5b d1 0c Write 0 to RAM[r5++] until r5 > acc (16 times)
1d: 5f 80 00 e0 08 1d: 5f 80 00 e0 08 Set OUT0 low
1e: 5f 00 1e fd 0c 1e: 5f 00 1e fd 0c JP 1E, infinite loop
Command is 00: Set bank to 0 and fill from 0 to 0x1fff with r2.b
1f: 5f 80 01 a0 0c 1f: 5f 80 01 a0 0c
20: df 20 00 cb 08 20: df 20 00 cb 08 r2 = RAM[1]
21: 5c 08 00 b3 0c 21: 5c 08 00 b3 0c
22: 5f 80 03 00 0c 22: 5f 80 03 00 0c Write 3 to RAM[1fff] m_bankctrl
23: 5c 08 00 b3 0c 23: 5c 08 00 b3 0c
24: 5f 80 00 80 0c 24: 5f 80 00 80 0c Write 3 to RAM[1fff] m_bankctrl
25: 5c 00 00 33 0c 25: 5c 00 00 33 0c
26: 5c 08 00 93 0c 26: 5c 08 00 93 0c Write 0 to RAM[0]
27: 9f 91 ff cf 0e 27: 9f 91 ff cf 0e Write 0 to RAM[1fff] m_bankctrl, select vram, r1 = fff << 1 = 1ffe
28: 5c 84 00 20 0c 28: 5c 84 00 20 0c
29: 84 00 00 b3 0c 29: 84 00 00 b3 0c ExtAddr = r0
2a: 49 10 69 d1 0c 2a: 49 10 69 d1 0c Write r2 to RAM[r0++] while r0 < r1
2b: 5f 80 00 e0 08 2b: 5f 80 00 e0 08 Set OUT0 low
2c: 5f 00 2c fd 0c 2c: 5f 00 2c fd 0c JP 2C, infinite loop
Command is 01: Set banks to 1 and fill from 0 to 0x1fff with r2.b
2d: 5f 80 01 a0 0c 2d: 5f 80 01 a0 0c
2e: df 20 00 cb 08 2e: df 20 00 cb 08 r2 = RAM(1)
2f: 5c 08 00 b3 0c 2f: 5c 08 00 b3 0c
30: 5f 80 03 00 0c 30: 5f 80 03 00 0c Write 3 to RAM[1fff] m_bankctrl
31: 5c 00 00 b3 0c 31: 5c 00 00 b3 0c
32: 5f 80 01 00 0c 32: 5f 80 01 00 0c Write 3 to RAM[0]
33: 5c 08 00 b3 0c 33: 5c 08 00 b3 0c
34: 5f 80 00 80 0c 34: 5f 80 00 80 0c Write 1 to RAM[1fff] m_bankctrl
35: 5c 00 00 33 0c 35: 5c 00 00 33 0c
36: 5c 08 00 93 0c 36: 5c 08 00 93 0c Write 0 to RAM[0]
37: 9f 91 ff cf 0e 37: 9f 91 ff cf 0e Write 0 to RAM[1fff] m_bankctrl, select vram, r1 = fff << 1 = 1ffe
38: 5c 84 00 20 0c 38: 5c 84 00 20 0c
39: 84 00 00 b3 0c 39: 84 00 00 b3 0c
3a: 49 10 79 d1 0c 3a: 49 10 79 d1 0c Write r2 to RAM[r0++] while r0 < r1
3b: 5f 80 00 e0 08 3b: 5f 80 00 e0 08 Set OUT0 low
3c: 5f 00 3c fd 0c 3c: 5f 00 3c fd 0c JP 3C, infinite loop
3d: ff ff ff ff ff
3e: ff ff ff ff ff 3d: ff ff ff ff ff Garbage
3f: ff ff ff ff ff 3e: ff ff ff ff ff Garbage
3f: ff ff ff ff ff Garbage
***************************************************************************/ ***************************************************************************/
@ -92,6 +102,7 @@ Notes:
#include "emupal.h" #include "emupal.h"
#include "speaker.h" #include "speaker.h"
#include "tilemap.h" #include "tilemap.h"
#include "multibyte.h"
// configurable logging // configurable logging
@ -120,7 +131,7 @@ public:
m_gfxdecode(*this, "gfxdecode"), m_gfxdecode(*this, "gfxdecode"),
m_palette(*this, "palette"), m_palette(*this, "palette"),
m_vram(*this, "vram%u", 0U, 0x2000U, ENDIANNESS_LITTLE), m_vram(*this, "vram%u", 0U, 0x2000U, ENDIANNESS_LITTLE),
m_unkram(*this, "unkram", 0x800, ENDIANNESS_LITTLE), m_pmcram(*this, "pmcram", 0x800, ENDIANNESS_LITTLE), // Might be an unused area of VRAM
m_rombank(*this, "rombank"), m_rombank(*this, "rombank"),
m_tilesrom(*this, "tiles") m_tilesrom(*this, "tiles")
{ } { }
@ -138,7 +149,7 @@ private:
required_device<palette_device> m_palette; required_device<palette_device> m_palette;
memory_share_array_creator<uint8_t, 2> m_vram; memory_share_array_creator<uint8_t, 2> m_vram;
memory_share_creator<uint8_t> m_unkram; memory_share_creator<uint8_t> m_pmcram;
required_memory_bank m_rombank; required_memory_bank m_rombank;
required_region_ptr<uint8_t> m_tilesrom; required_region_ptr<uint8_t> m_tilesrom;
@ -231,12 +242,21 @@ void hexion_state::bankswitch_w(uint8_t data)
// bits 0-3 select ROM bank // bits 0-3 select ROM bank
m_rombank->set_entry(data & 0x0f); m_rombank->set_entry(data & 0x0f);
// does bit 6 trigger the 052591? // bit 6 triggers the 052591
if (data & 0x40) if (data & 0x40)
{ {
int bank = m_unkram[0] & 1; uint8_t command = m_pmcram[0];
memset(m_vram[bank], m_unkram[1], 0x2000); if (command <= 1)
m_bg_tilemap[bank]->mark_all_dirty(); {
memset(m_vram[command], m_pmcram[1], 0x2000);
m_bg_tilemap[command]->mark_all_dirty();
}
else
{
uint8_t bank = m_pmcram[4] & 1;
memset(m_vram[bank] + (get_u16le(&m_pmcram[2]) & 0x1fff), 0, 16);
m_bg_tilemap[bank]->mark_all_dirty();
}
} }
// bit 7 = PMC-BK // bit 7 = PMC-BK
m_pmcbank = (data & 0x80) >> 7; m_pmcbank = (data & 0x80) >> 7;
@ -258,7 +278,7 @@ uint8_t hexion_state::bankedram_r(offs_t offset)
} }
else if (m_bankctrl == 2 && offset < 0x800) else if (m_bankctrl == 2 && offset < 0x800)
{ {
return m_unkram[offset]; return m_pmcram[offset];
} }
else else
{ {
@ -289,8 +309,8 @@ void hexion_state::bankedram_w(offs_t offset, uint8_t data)
{ {
if (m_pmcbank) if (m_pmcbank)
{ {
LOGBANKEDRAM("%s: unkram_w offset %04x, data %02x, bankctrl = %02x\n", m_maincpu->pc(), offset, data, m_bankctrl); LOGBANKEDRAM("%s: pmcram_w offset %04x, data %02x, bankctrl = %02x\n", m_maincpu->pc(), offset, data, m_bankctrl);
m_unkram[offset] = data; m_pmcram[offset] = data;
} }
else else
LOGBANKEDRAM("%04x pmc internal ram %04x = %02x\n", m_maincpu->pc(), offset, data); LOGBANKEDRAM("%04x pmc internal ram %04x = %02x\n", m_maincpu->pc(), offset, data);

View File

@ -11,6 +11,9 @@
Revisions: Revisions:
31-01-2024 Furrtek
- updated PMCU collision check code to match what the original program does
05-10-2002 Acho A. Tang 05-10-2002 Acho A. Tang
- simulated PMCU protection(guess only) - simulated PMCU protection(guess only)
- changed priority scheme to fix graphics in 3D levels - changed priority scheme to fix graphics in 3D levels
@ -34,6 +37,7 @@
#include "sound/ymopl.h" #include "sound/ymopl.h"
#include "emupal.h" #include "emupal.h"
#include "speaker.h" #include "speaker.h"
#include "multibyte.h"
namespace { namespace {
@ -63,6 +67,7 @@ private:
/* misc */ /* misc */
int m_rambank = 0; int m_rambank = 0;
int m_pmcbank = 0; int m_pmcbank = 0;
uint8_t m_pmcpc = 0;
int m_video_enable = 0; int m_video_enable = 0;
int m_old_3f90 = 0; int m_old_3f90 = 0;
@ -85,7 +90,7 @@ private:
virtual void machine_start() override; virtual void machine_start() override;
virtual void machine_reset() override; virtual void machine_reset() override;
uint32_t screen_update_spy(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); uint32_t screen_update_spy(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect);
void spy_collision( ); void pmc_run( );
void volume_callback0(uint8_t data); void volume_callback0(uint8_t data);
void volume_callback1(uint8_t data); void volume_callback1(uint8_t data);
K052109_CB_MEMBER(tile_callback); K052109_CB_MEMBER(tile_callback);
@ -172,8 +177,7 @@ uint8_t spy_state::spy_bankedram1_r(offs_t offset)
} }
else else
{ {
//logerror("%04x read pmc internal ram %04x\n", m_maincpu->pc(), offset); return 0; // PMC internal RAM can't be read back
return 0;
} }
} }
else else
@ -193,79 +197,90 @@ void spy_state::spy_bankedram1_w(offs_t offset, uint8_t data)
//logerror("%04x pmcram %04x = %02x\n", m_maincpu->pc(), offset, data); //logerror("%04x pmcram %04x = %02x\n", m_maincpu->pc(), offset, data);
m_pmcram[offset] = data; m_pmcram[offset] = data;
} }
//else else
//logerror("%04x pmc internal ram %04x = %02x\n", m_maincpu->pc(), offset, data); {
// Set initial PMC PC
m_pmcpc = data & 0x3f;
}
} }
else else
m_ram[offset] = data; m_ram[offset] = data;
} }
/* /*
this is the data written to internal ram on startup: This is the 052591 PMC code loaded at startup, it contains both projection and collision check programs.
00: e7 7e 38 fc 08 See https://github.com/furrtek/SiliconRE/tree/master/Konami/052591 for details
01: df 36 38 dc 00
02: df 12 3a dc 00 Coordinate projection routine:
03: df 00 38 dc 08 00: e7 7e 38 fc 08 Clear r7
01: df 36 38 dc 00 r3.w = RAM[0]
02: df 12 3a dc 00 r1.w = RAM[2]
03: df 00 38 dc 08 r0 = RAM[4]
04: 1f 7e 00 db 00 04: 1f 7e 00 db 00
05: 26 fe 00 ff 0c 05: 26 fe 00 ff 0c Acc.w = RAM[5],00
06: 89 03 34 fc 0d 06: 89 03 34 fc 0d 1+16 division steps, CALL 34...
07: 81 03 34 fc 09 07: 81 03 34 fc 09 add/sub, CALL 34
08: 81 03 34 fc 09 08: 81 03 34 fc 09 add/sub, CALL 34
09: 81 03 34 fc 09 09: 81 03 34 fc 09 add/sub, CALL 34
0a: 81 03 2f fc 09 0a: 81 03 2f fc 09 add/sub, CALL 34
0b: cc 36 0e d9 08 0b: cc 36 0e d9 08 r3--, JP 0e if 0
0c: 84 7e 00 ab 0c 0c: 84 7e 00 ab 0c
0d: 5f 7e 03 cd 08 0d: 5f 7e 03 cd 08 JP 03
0e: 7f 80 fe ef 08
0f: 5f 7e 0f fd 08 0e: 7f 80 fe ef 08 Set OUT0 low
10: e7 7e 38 fc 08 0f: 5f 7e 0f fd 08 JP 0f, infinite loop
Collision check routine:
10: e7 7e 38 fc 08 r0.w = RAM[0]
11: df 00 3a dc 00 11: df 00 3a dc 00
12: df 12 0e d9 08 12: df 12 0e d9 08 r1 = RAM[2], JP 0e if 0
13: df ec 10 e0 0c 13: df ec 10 e0 0c r6 = 10
14: 1f fe 03 e0 0c 14: 1f fe 03 e0 0c Acc = 3
15: df fe 03 e0 0c 15: df fe 03 e0 0c r7 = 3
16: dc 5e 3e fc 08 16: dc 5e 3e fc 08 r5 = 3
17: df 12 2b d9 08 17: df 12 2b d9 08 r1 = RAM[r6++], JP 2b if 0
18: 67 25 38 fc 0c 18: 67 25 38 fc 0c r2 = 8000 ?
19: df 12 3c dc 00 19: df 12 3c dc 00
1a: df 36 00 db 00 1a: df 36 00 db 00
1b: c1 14 00 fb 08 1b: c1 14 00 fb 08 r1 += r2
1c: c1 34 38 fc 08 1c: c1 34 38 fc 08 r3 += r2
1d: c5 22 37 dc 00 1d: c5 22 37 dc 00
1e: cd 12 3c dc 04 1e: cd 12 3c dc 04
1f: c5 46 3b dc 00 1f: c5 46 3b dc 00
20: cd 36 00 db 04 20: cd 36 00 db 04
21: 49 16 ed f9 0c 21: 49 16 ed f9 0c JP 2d if r1 < r3
22: c9 18 ea f9 0c 22: c9 18 ea f9 0c JP 2a if r1 < r4
23: dc 12 2a f9 08 23: dc 12 2a f9 08
24: cc 5a 26 f9 08 24: cc 5a 26 f9 08 r5--, JP 26 if 0
25: 5f 7e 18 fd 08 25: 5f 7e 18 fd 08 JP 18
26: 5a 7e 32 f8 08 26: 5a 7e 32 f8 08 Clear RAM(r7++) if Acc is 0
27: 84 6c 33 9c 0c 27: 84 6c 33 9c 0c RAM(r6++) = Acc
28: cc 00 0e d9 08 28: cc 00 0e d9 08 r0--, JP 0e if 0
29: 5f 7e 14 fd 08 29: 5f 7e 14 fd 08
2a: 0a 7e 24 fd 08 2a: 0a 7e 24 fd 08
2b: c5 ec 0d e0 0c 2b: c5 ec 0d e0 0c r6 += d, next object
2c: 5f 7e 28 fd 08 2c: 5f 7e 28 fd 08
2d: dc 16 00 fb 08 2d: dc 16 00 fb 08 r1 = r3
2e: dc 44 22 fd 08 2e: dc 44 22 fd 08 r4 = r2
2f: cd fe 02 e0 0c 2f: cd fe 02 e0 0c r7 -= 3
30: 84 7e 00 bb 0c 30: 84 7e 00 bb 0c
31: 5a 7e 00 73 08 31: 5a 7e 00 73 08
32: 84 7e 00 9b 0c 32: 84 7e 00 9b 0c
33: 5a 7e 00 36 08 33: 5a 7e 00 36 08
34: 81 03 00 fb 09
35: 81 03 00 fb 09 34: 81 03 00 fb 09 add/sub
36: 81 03 00 fe 09 35: 81 03 00 fb 09 add/sub
37: cd fe 01 e0 0c 36: 81 03 00 fe 09 add/sub, ret
37: cd fe 01 e0 0c r7 -= 2
38: 84 7e 00 ab 0c 38: 84 7e 00 ab 0c
39: 5f 7e 00 db 00 39: 5f 7e 00 db 00 Set MSB as RAM(r7)
3a: 84 7e 3f ad 0c 3a: 84 7e 3f ad 0c
3b: cd ec 01 e0 0c 3b: cd ec 01 e0 0c r6 -= 2
3c: 84 6c 00 ab 0c 3c: 84 6c 00 ab 0c
3d: 5f 7e 00 db 00 3d: 5f 7e 00 db 00 Set MSB as RAM(r6)
3e: 84 6c 00 ab 0c 3e: 84 6c 00 ab 0c
3f: 5f 7e 00 ce 08 3f: 5f 7e 00 ce 08 ret
*/ */
void spy_state::bankswitch_w(uint8_t data) void spy_state::bankswitch_w(uint8_t data)
@ -285,66 +300,20 @@ void spy_state::bankswitch_w(uint8_t data)
membank("bank1")->set_entry(bank); membank("bank1")->set_entry(bank);
} }
void spy_state::spy_collision( ) void spy_state::pmc_run( )
{ {
#define MAX_SPRITES 64 constexpr uint16_t MAX_SPRITES = 64;
#define DEF_NEAR_PLANE 0x6400 constexpr uint16_t DEF_NEAR_PLANE = 0x6400;
#define NEAR_PLANE_ZOOM 0x0100
#define FAR_PLANE_ZOOM 0x0000 if (m_pmcpc == 0x00)
int op1, x1, w1, z1, d1, y1, h1;
int op2, x2, w2, z2, d2, y2, h2;
int mode, i, loopend, nearplane;
mode = m_pmcram[0x1];
op1 = m_pmcram[0x2];
if (op1 == 1)
{ {
x1 = (m_pmcram[0x3] << 8) + m_pmcram[0x4]; // Projection program
w1 = (m_pmcram[0x5] << 8) + m_pmcram[0x6]; // Basically divides a list of 16-bit words by a constant, results are 8.8 fixed point
z1 = (m_pmcram[0x7] << 8) + m_pmcram[0x8]; uint16_t loopend, nearplane;
d1 = (m_pmcram[0x9] << 8) + m_pmcram[0xa]; uint32_t op;
y1 = (m_pmcram[0xb] << 8) + m_pmcram[0xc];
h1 = (m_pmcram[0xd] << 8) + m_pmcram[0xe]; loopend = get_u16be(&m_pmcram[0]);
nearplane = get_u16be(&m_pmcram[2]);
for (i = 16; i < 14 * MAX_SPRITES + 2; i += 14)
{
op2 = m_pmcram[i];
if (op2 || mode == 0x0c)
{
x2 = (m_pmcram[i + 0x1] << 8) + m_pmcram[i + 0x2];
w2 = (m_pmcram[i + 0x3] << 8) + m_pmcram[i + 0x4];
z2 = (m_pmcram[i + 0x5] << 8) + m_pmcram[i + 0x6];
d2 = (m_pmcram[i + 0x7] << 8) + m_pmcram[i + 0x8];
y2 = (m_pmcram[i + 0x9] << 8) + m_pmcram[i + 0xa];
h2 = (m_pmcram[i + 0xb] << 8) + m_pmcram[i + 0xc];
/*
The mad scientist's laser truck has both a high sprite center and a small height value.
It has to be measured from the ground to detect correctly.
*/
if (w2 == 0x58 && d2 == 0x04 && h2 == 0x10 && y2 == 0x30)
h2 = y2;
// what other sprites fall into:
if ((abs(x1 - x2) < w1 + w2) && (abs(z1 - z2) < d1 + d2) && (abs(y1 - y2) < h1 + h2))
{
m_pmcram[0xf] = 0;
m_pmcram[i + 0xd] = 0;
}
else
m_pmcram[i + 0xd] = 1;
}
}
}
else if (op1 > 1)
{
/*
The PMCU also projects geometries to screen coordinates. Unfortunately I'm unable to figure
the scale factors from the PMCU code. Plugging 0 and 0x100 to the far and near planes seems
to do the trick though.
*/
loopend = (m_pmcram[0] << 8) + m_pmcram[1];
nearplane = (m_pmcram[2] << 8) + m_pmcram[3];
// fail safe // fail safe
if (loopend > MAX_SPRITES) if (loopend > MAX_SPRITES)
@ -354,16 +323,65 @@ void spy_state::spy_collision( )
loopend = (loopend << 1) + 4; loopend = (loopend << 1) + 4;
for (i = 4; i < loopend; i += 2) for (uint16_t i = 4; i < loopend; i += 2)
{ {
op2 = (m_pmcram[i] << 8) + m_pmcram[i + 1]; op = get_u16be(&m_pmcram[i]);
op2 = (op2 * (NEAR_PLANE_ZOOM - FAR_PLANE_ZOOM)) / nearplane + FAR_PLANE_ZOOM; op = (op << 8) / nearplane;
m_pmcram[i] = op2 >> 8; put_u16be(&m_pmcram[i], op);
m_pmcram[i + 1] = op2 & 0xff;
} }
memset(m_pmcram + loopend, 0, 0x800 - loopend); // clean up for next frame memset(m_pmcram + loopend, 0, 0x800 - loopend); // clean up for next frame
} }
else
{
// Collision check program
if (!m_pmcram[0x2])
return;
const uint16_t count = get_u16be(&m_pmcram[0]);
for (uint16_t i = 16; i < 16 + (14 * count); i += 14)
{
if (!m_pmcram[i])
continue;
// Check all 3 dimensions
uint8_t tests_failed = 3;
for (uint16_t j = 0; j < 3 * 4; j += 4)
{
const int16_t a_pos = get_s16be(&m_pmcram[j + 3]); // Object A center position
const int16_t a_size = get_s16be(&m_pmcram[j + 5]); // Object A half size
const int16_t b_pos = get_s16be(&m_pmcram[i + j + 1]); // Object B center position
const int16_t b_size = get_s16be(&m_pmcram[i + j + 3]); // Object B half size
const int16_t a_max = a_pos + a_size; // Object A right edge
const int16_t a_min = a_pos - a_size; // Object A left edge
const int16_t b_max = b_pos + b_size; // Object B right edge
const int16_t b_min = b_pos - b_size; // Object B left edge
if (b_min > a_min)
{
// Object B left edge is > object A left edge
// Checks if A.left < B.left <= A.right
if (a_max >= b_min)
tests_failed--;
}
else
{
// Object B left edge is <= object A left edge
// Checks if B.left <= A.left <= B.right
if (b_max >= a_min)
tests_failed--;
}
}
// Mark objects as collided or not
if (!tests_failed) m_pmcram[0xf] = 0;
m_pmcram[i + 0xd] = tests_failed;
}
}
} }
@ -423,26 +441,10 @@ void spy_state::spy_3f90_w(uint8_t data)
/* bit 7 = PMC-BK */ /* bit 7 = PMC-BK */
m_pmcbank = (data & 0x80) >> 7; m_pmcbank = (data & 0x80) >> 7;
//logerror("%04x: 3f90_w %02x\n", m_maincpu->pc(), data);
/* bit 6 = PMC-START */ /* bit 6 = PMC-START */
if ((data & 0x40) && !(m_old_3f90 & 0x40)) if ((data & 0x40) && !(m_old_3f90 & 0x40))
{ {
/* we should handle collision here */ pmc_run();
//AT
if (0)
{
int i;
logerror("collision test:\n");
for (i = 0; i < 0xfe; i++)
{
logerror("%02x ", m_pmcram[i]);
if (i == 0x0f || (i > 0x10 && (i - 0x10) % 14 == 13))
logerror("\n");
}
}
spy_collision();
//ZT
m_maincpu->set_input_line(M6809_FIRQ_LINE, HOLD_LINE); m_maincpu->set_input_line(M6809_FIRQ_LINE, HOLD_LINE);
} }
@ -601,6 +603,7 @@ void spy_state::machine_start()
save_item(NAME(m_paletteram)); save_item(NAME(m_paletteram));
save_item(NAME(m_rambank)); save_item(NAME(m_rambank));
save_item(NAME(m_pmcbank)); save_item(NAME(m_pmcbank));
save_item(NAME(m_pmcpc));
save_item(NAME(m_video_enable)); save_item(NAME(m_video_enable));
save_item(NAME(m_old_3f90)); save_item(NAME(m_old_3f90));
save_item(NAME(m_pmcram)); save_item(NAME(m_pmcram));
@ -610,6 +613,7 @@ void spy_state::machine_reset()
{ {
m_rambank = 0; m_rambank = 0;
m_pmcbank = 0; m_pmcbank = 0;
m_pmcpc = 0;
m_video_enable = 0; m_video_enable = 0;
m_old_3f90 = -1; m_old_3f90 = -1;
} }

View File

@ -49,6 +49,7 @@
#include "emupal.h" #include "emupal.h"
#include "screen.h" #include "screen.h"
#include "speaker.h" #include "speaker.h"
#include "multibyte.h"
//#define VERBOSE 1 //#define VERBOSE 1
#include "logmacro.h" #include "logmacro.h"
@ -164,8 +165,8 @@ private:
void thunderx_videobank_w(uint8_t data); void thunderx_videobank_w(uint8_t data);
void thunderx_1f98_w(uint8_t data); void thunderx_1f98_w(uint8_t data);
void run_collisions(int s0, int e0, int s1, int e1, int cm, int hm); //void run_collisions(int s0, int e0, int s1, int e1, int cm, int hm);
void calculate_collisions(); void pmc_run();
void thunderx_map(address_map &map) ATTR_COLD; void thunderx_map(address_map &map) ATTR_COLD;
}; };
@ -258,8 +259,7 @@ uint8_t thunderx_state::pmc_r(offs_t offset)
} }
else else
{ {
LOG("%04x read pmc internal ram %04x\n", m_audiocpu->pc(), offset); return 0; // PMC internal RAM can't be read back
return 0;
} }
} }
@ -272,141 +272,86 @@ void thunderx_state::pmc_w(offs_t offset, uint8_t data)
} }
else else
{ {
LOG("%04x pmc internal ram %04x = %02x\n", m_audiocpu->pc(), offset, data); LOG("%04x pmc set initial PC %04x = %02x\n", m_audiocpu->pc(), offset, data);
// thunderx only uses one PMC program which has its entry point at address 01
} }
} }
/* /*
this is the data written to internal ram on startup: This is the 052591 PMC code loaded at startup, it contains a collision check program.
See https://github.com/furrtek/SiliconRE/tree/master/Konami/052591 for details
Japan version US version Japan version US version
00: e7 00 00 ad 08 e7 00 00 ad 08 00: e7 00 00 ad 08 e7 00 00 ad 08 JP 00, infinite loop
01: 5f 80 05 a0 0c 1f 80 05 a0 0c LDW ACC,RAM+05 01: 5f 80 05 a0 0c 1f 80 05 a0 0c Set ext address to 5
02: 42 7e 00 8b 04 regE 02: 42 7e 00 8b 04
03: df 00 e2 8b 08 df 8e 00 cb 04 regE 03: df 00 e2 8b 08 df 8e 00 cb 04 r0.b or r0.w = RAM[5]
04: 5f 80 06 a0 0c 5f 80 07 a0 0c LDB ACC,RAM+07 04: 5f 80 06 a0 0c 5f 80 07 a0 0c Set ext address to 6 or 7
05: df 7e 00 cb 08 df 7e 00 cb 08 LDB R7,[Rx] 05: df 7e 00 cb 08 df 7e 00 cb 08 r7.b = RAM[6 or 7]
06: 1b 80 00 a0 0c 1b 80 00 a0 0c LDPTR #0 PTR2,RAM+00 06: 1b 80 00 a0 0c 1b 80 00 a0 0c Set ext address to r0
07: df 10 00 cb 08 df 10 00 cb 08 LDB R1,[PTR] (fl) LDB R1,[Rx] (flags) 07: df 10 00 cb 08 df 10 00 cb 08 r1.b = RAM[r0] (flags)
08: 5f 80 03 a0 0c 5f 80 03 a0 0c LDB R0,[3] (cm) LDB ACC,RAM+03 load collide mask 08: 5f 80 03 a0 0c 5f 80 03 a0 0c Set ext address to 3
09: 1f 20 00 cb 08 1f 20 00 cb 08 LD CMP2,R0 test (AND) R1 vs ACC 09: 1f 20 00 cb 08 1f 20 00 cb 08 acc.b = RAM[3] (collide mask)
0a: c4 00 00 ab 0c c4 00 00 ab 0c INC PTR LEA Rx,[++PTR2] 0a: c4 00 00 ab 0c c4 00 00 ab 0c INC r0, set ext address to r0
0b: df 20 00 cb 08 df 20 00 cb 08 LDB R2,[PTR] (w) LDB R2,[Rx] (width) 0b: df 20 00 cb 08 df 20 00 cb 08 r2.b = RAM[r0] (width)
0c: c4 00 00 ab 0c c4 00 00 ab 0c INC PTR LEA Rx,[++PTR2] 0c: c4 00 00 ab 0c c4 00 00 ab 0c INC r0, set ext address to r0
0d: df 30 00 cb 08 df 30 00 cb 08 LDB R3,[PTR] (h) LDB R3,[Rx] (height) 0d: df 30 00 cb 08 df 30 00 cb 08 r3.b = RAM[r0] (height)
0e: c4 00 00 ab 0c c4 00 00 ab 0c INC PTR LEA Rx,[++PTR2] 0e: c4 00 00 ab 0c c4 00 00 ab 0c INC r0, set ext address to r0
0f: df 40 00 cb 08 df 40 00 cb 08 LDB R4,[PTR] (x) LDB R4,[Rx] (x) 0f: df 40 00 cb 08 df 40 00 cb 08 r4.b = RAM[r0] (x)
10: c4 00 00 ab 0c c4 00 00 ab 0c INC PTR LEA Rx,[++PTR2] 10: c4 00 00 ab 0c c4 00 00 ab 0c INC r0, set ext address to r0
11: df 50 00 cb 08 df 50 00 cb 08 LDB R5,[PTR] (y) LDB R5,[Rx] (y) 11: df 50 00 cb 08 df 50 00 cb 08 r5.b = RAM[r0] (y)
12: 60 22 35 e9 08 60 22 36 e9 08 BANDZ CMP2,R1,36 R2/R1, BEQ 36 12: 60 22 35 e9 08 60 22 36 e9 08 JP 35 or 36 if r1 AND acc == 0
13: 44 0e 00 ab 08 44 0e 00 ab 08 MOVE PTR,INNER LEA Rx,[PTR,0] load flags 13: 44 0e 00 ab 08 44 0e 00 ab 08 Set ext address to r7
14: df 60 00 cb 08 df 60 00 cb 08 LDB R6,[PTR] (fl) LDB R6,[Rx] 14: df 60 00 cb 08 df 60 00 cb 08 r6.b = RAM[r7] (flags)
15: 5f 80 04 a0 0c 5f 80 04 a0 0c LDB R0,[4] (hm) LDB ACC,RAM+04 load hit mask 15: 5f 80 04 a0 0c 5f 80 04 a0 0c Set ext address to 4
16: 1f 60 00 cb 08 1f 60 00 cb 08 LD CMP6,R0 test R6 and ACC (AND) 16: 1f 60 00 cb 08 1f 60 00 cb 08 acc.b = RAM[4] (hit mask)
17: 60 6c 31 e9 08 60 6c 32 e9 08 BANDZ CMP6,R6,32 R6, BEQ 32 17: 60 6c 31 e9 08 60 6c 32 e9 08 JP 32 if r6 AND acc == 0
18: 45 8e 01 a0 0c 45 8e 01 a0 0c LDB R0,[INNER+1] LDB Ry,[PTR,1] (width) 18: 45 8e 01 a0 0c 45 8e 01 a0 0c Set ext address to r7 + 1
19: c5 64 00 cb 08 c5 64 00 cb 08 ADD ACC,R0,R2 R6 = ADD Ry,R2 19: c5 64 00 cb 08 c5 64 00 cb 08 r6.b = RAM[r7 + 1] + r2 (add widths together)
1a: 45 8e 03 a0 0c 45 8e 03 a0 0c LDB R0,[INNER+3] LDB Ry,[PTR,3] (x) 1a: 45 8e 03 a0 0c 45 8e 03 a0 0c Set ext address to r7 + 3
1b: 67 00 00 cb 0c 67 00 00 cb 0c MOV CMP,R0 ??? DEC Ry 1b: 67 00 00 cb 0c 67 00 00 cb 0c acc = RAM[r7 + 3] - r4 (x1 - x0)
1c: 15 48 5d c9 0c 15 48 5e c9 0c SUB CMP,R4 ; BGE 1E SUB R4,Ry; Bcc 1E 1c: 15 48 5d c9 0c 15 48 5e c9 0c JP 1D or 1E if positive
1d: 12 00 00 eb 0c 12 00 00 eb 0c NEG CMP ??? NEG Ry 1d: 12 00 00 eb 0c 12 00 00 eb 0c NEG acc
1e: 48 6c 71 e9 0c 48 6c 72 e9 0c B (CMP > ACC) 32 R6, BLO 32 1e: 48 6c 71 e9 0c 48 6c 72 e9 0c JP 31 or 32 if r6 < acc
1f: 45 8e 02 a0 0c 45 8e 02 a0 0c LDB R0,[INNER+2] LDB Ry,[PTR,2] (height) 1f: 45 8e 02 a0 0c 45 8e 02 a0 0c Set ext address to r7 + 2
20: c5 66 00 cb 08 c5 66 00 cb 08 ADD ACC,R0,R3 R6 = ADD Ry,R3 20: c5 66 00 cb 08 c5 66 00 cb 08 r6.b = RAM[r7 + 2] + r3 (add heights together)
21: 45 8e 04 a0 0c 45 8e 04 a0 0c LDB R0,[INNER+4] LDB Ry,[PTR,4] (y) 21: 45 8e 04 a0 0c 45 8e 04 a0 0c Set ext address to r7 + 4
22: 67 00 00 cb 0c 67 00 00 cb 0c MOV CMP,R0 ??? DEC Ry 22: 67 00 00 cb 0c 67 00 00 cb 0c acc = RAM[r7 + 4] - r5 (y1 - y0)
23: 15 5a 64 c9 0c 15 5a 65 c9 0c SUB CMP,R5 ; BGE 25 SUB R5,Ry; Bcc 25 23: 15 5a 64 c9 0c 15 5a 65 c9 0c JP 24 or 25 if positive
24: 12 00 00 eb 0c 12 00 00 eb 0c NEG CMP ??? NEG Ry 24: 12 00 00 eb 0c 12 00 00 eb 0c NEG acc
25: 48 6c 71 e9 0c 48 6c 72 e9 0c B (CMP > ACC) 32 R6, BLO 32 25: 48 6c 71 e9 0c 48 6c 72 e9 0c JP 31 or 32 if r6 < acc
26: e5 92 9b e0 0c e5 92 9b e0 0c AND R1,#$9B 26: e5 92 9b e0 0c e5 92 9b e0 0c AND R1,#$9B
27: dd 92 10 e0 0c dd 92 10 e0 0c OR R1,#$10 27: dd 92 10 e0 0c dd 92 10 e0 0c OR R1,#$10
28: 5c fe 00 a0 0c 5c fe 00 a0 0c ??? STB [PTR,0] 28: 5c fe 00 a0 0c 5c fe 00 a0 0c Set ext address to r7
29: df 60 00 d3 08 df 60 00 d3 08 LDB R6, 29: df 60 00 d3 08 df 60 00 d3 08 r6.b = RAM[r7] (flags)
2a: e5 ec 9f e0 0c e5 ec 9f e0 0c AND R6,#$9F 2a: e5 ec 9f e0 0c e5 ec 9f e0 0c AND r6,#$9F
2b: dd ec 10 00 0c dd ec 10 00 0c OR R6,#$10 2b: dd ec 10 00 0c dd ec 10 00 0c RAM[r7] = r6 | #$10
2c: 25 ec 04 c0 0c 25 ec 04 c0 0c STB R6,[PTR2,-4] 2c: 25 ec 04 c0 0c 25 ec 04 c0 0c acc = r6 & 4
2d: 18 82 00 00 0c 18 82 00 00 0c 2d: 18 82 00 00 0c 18 82 00 00 0c OR acc,r1
2e: 4d 80 03 a0 0c 4d 80 03 a0 0c RAM+03 2e: 4d 80 03 a0 0c 4d 80 03 a0 0c Set ext address to r7 - 4
2f: df e0 e6 e0 0c df e0 36 e1 0c 2f: df e0 e6 e0 0c df e0 36 e1 0c r6 = #$E6
30: 49 60 75 f1 08 49 60 76 f1 08 Jcc 36 30: 49 60 75 f1 08 49 60 76 f1 08 JP 35 or 36 if r0 < r6
31: 67 00 35 cd 08 67 00 36 cd 08 Jcc 36 31: 67 00 35 cd 08 67 00 36 cd 08 Write acc to [r7 - 4], JP 35
32: c5 fe 05 e0 0c c5 fe 05 e0 0c ADD R7,R7,5 ADD regE,#5 32: c5 fe 05 e0 0c c5 fe 05 e0 0c r7 += 5, next object in set 1
33: 5f 80 02 a0 0c 5f 80 02 a0 0c LDB R0, [2] LDB ACC,RAM+02 33: 5f 80 02 a0 0c 5f 80 02 a0 0c Set ext address to 2
34: 1f 00 00 cb 08 1f 00 00 cb 08 LCMP CMP0,R0 34: 1f 00 00 cb 08 1f 00 00 cb 08 acc.b = RAM[2]
35: 48 6e 52 c9 0c 48 6e 53 c9 0c BNEQ CMP0,R7, 33 R6/R7, BLO 13 35: 48 6e 52 c9 0c 48 6e 53 c9 0c JP 12 or 13 if r7 < acc
36: c4 00 00 ab 0c c4 00 00 ab 0c INC PTR LEA Rx,[++PTR2] 36: c4 00 00 ab 0c c4 00 00 ab 0c INC r0, next object in set 0
37: 27 00 00 ab 0c 27 00 00 ab 0c 37: 27 00 00 ab 0c 27 00 00 ab 0c Set ext address to 0
38: 42 00 00 8b 04 42 00 00 8b 04 MOVE PTR, OUTER 38: 42 00 00 8b 04 42 00 00 8b 04 acc.w = RAM[0]
39: 1f 00 00 cb 00 1f 00 00 cb 00 LCMP CMP0 ?? test PTR2 vs ACC 39: 1f 00 00 cb 00 1f 00 00 cb 00
3a: 48 00 43 c9 00 48 00 44 c9 00 BLT 4 BLT 04 next in set 0 3a: 48 00 43 c9 00 48 00 44 c9 00 JP 3 or 4 if r0 < acc
3b: 5f fe 00 e0 08 5f fe 00 e0 08 3b: 5f fe 00 e0 08 5f fe 00 e0 08 Set OUT0 low
3c: 5f 7e 00 ed 08 5f 7e 00 ed 08 3c: 5f 7e 00 ed 08 5f 7e 00 ed 08 JP 0
3d: ff 04 00 ff 06 ff 04 00 ff 06 STOP STOP 3d: ff 04 00 ff 06 ff 04 00 ff 06 Garbage
3e: 05 07 ff 02 03 05 07 ff 02 03 3e: 05 07 ff 02 03 05 07 ff 02 03 Garbage
3f: 01 01 e0 02 6c 01 00 60 00 a0 3f: 01 01 e0 02 6c 01 00 60 00 a0 Garbage
03 6c 04 40 04 03 6c 04 40 04
*/ */
// run_collisions
//
// collide objects from s0 to e0 against
// objects from s1 to e1
//
// only compare objects with the specified bits (cm) set in their flags
// only set object 0's hit bit if (hm & 0x40) is true
//
// the data format is:
//
// +0 : flags
// +1 : width (4 pixel units)
// +2 : height (4 pixel units)
// +3 : x (2 pixel units) of center of object
// +4 : y (2 pixel units) of center of object
void thunderx_state::run_collisions( int s0, int e0, int s1, int e1, int cm, int hm )
{
for (uint8_t *p0 = &m_pmcram[s0]; p0 < &m_pmcram[e0]; p0 += 5)
{
// check valid
if (!(p0[0] & cm))
continue;
// get area
const int l0 = p0[3] - p0[1];
const int r0 = p0[3] + p0[1];
const int t0 = p0[4] - p0[2];
const int b0 = p0[4] + p0[2];
for (uint8_t *p1 = &m_pmcram[s1]; p1 < &m_pmcram[e1]; p1 += 5)
{
// check valid
if (!(p1[0] & hm))
continue;
// get area
const int l1 = p1[3] - p1[1];
const int r1 = p1[3] + p1[1];
const int t1 = p1[4] - p1[2];
const int b1 = p1[4] + p1[2];
// overlap check
if (l1 >= r0) continue;
if (l0 >= r1) continue;
if (t1 >= b0) continue;
if (t0 >= b1) continue;
// set flags
p0[0] = (p0[0] & 0x8f) | (p1[0] & 0x04) | 0x10;
p1[0] = (p1[0] & 0x8f) | 0x10;
}
}
}
// calculate_collisions
//
// emulates K052591 collision detection // emulates K052591 collision detection
void thunderx_state::calculate_collisions() void thunderx_state::pmc_run()
{ {
// the data at 0x00 to 0x06 defines the operation // the data at 0x00 to 0x06 defines the operation
// //
@ -423,18 +368,17 @@ void thunderx_state::calculate_collisions()
// 0x07 : byte : first byte of set 1 // 0x07 : byte : first byte of set 1
// //
// the operation is to intersect set 0 with set 1 // the operation is to intersect set 0 with set 1
// collide mask specifies objects to ignore // masks specify objects to ignore
// hit mask is 40 to set bit on object 0 and object 1
// hit mask is 20 to set bit on object 1 only
const int e0 = (m_pmcram[0] << 8) | m_pmcram[1]; const uint16_t e0 = get_u16be(&m_pmcram[0]);
const int e1 = m_pmcram[2]; const uint8_t e1 = m_pmcram[2];
int s0, s1; uint16_t s0, s1;
// Heuristic to determine version of program based on byte at 0x05
if (m_pmcram[5] < 16) if (m_pmcram[5] < 16)
{ {
// US Thunder Cross uses this form // US Thunder Cross uses this form
s0 = (m_pmcram[5] << 8) + m_pmcram[6]; s0 = get_u16be(&m_pmcram[5]);
s1 = m_pmcram[7]; s1 = m_pmcram[7];
} }
else else
@ -444,10 +388,44 @@ void thunderx_state::calculate_collisions()
s1 = m_pmcram[6]; s1 = m_pmcram[6];
} }
const int cm = m_pmcram[3]; const uint8_t cm = m_pmcram[3];
const int hm = m_pmcram[4]; const uint8_t hm = m_pmcram[4];
run_collisions(s0, e0, s1, e1, cm, hm); // collide objects from s0 to e0 against objects from s1 to e1
// only process objects with the specified bits (cm/hm) set in their flags
//
// the data format for each object is:
//
// +0 : flags
// +1 : width (4 pixel units)
// +2 : height (4 pixel units)
// +3 : x (2 pixel units) of center of object
// +4 : y (2 pixel units) of center of object
for (uint8_t *p0 = &m_pmcram[s0]; p0 < &m_pmcram[e0]; p0 += 5)
{
// check object 0 flags
if (!(p0[0] & cm))
continue;
for (uint8_t *p1 = &m_pmcram[s1]; p1 < &m_pmcram[e1]; p1 += 5)
{
// check object 1 flags
if (!(p1[0] & hm))
continue;
if (p1[1] + p0[1] < abs(p1[3] - p0[3])) continue;
if (p1[2] + p0[2] < abs(p1[4] - p0[4])) continue;
// set flags
p1[0] = (p1[0] & 0x8f) | 0x10;
if (p1 > (uint8_t*)0xe6) // This address value is hardcoded in the PMC program
p0[0] = (p0[0] & 0x9b) | (p1[0] & 0x04) | 0x10;
break;
}
}
// 100 cycle delay is arbitrary
m_thunderx_firq_timer->adjust(m_maincpu->cycles_to_attotime(100));
} }
void thunderx_state_base::scontra_1f98_w(uint8_t data) void thunderx_state_base::scontra_1f98_w(uint8_t data)
@ -475,10 +453,7 @@ void thunderx_state::thunderx_1f98_w(uint8_t data)
// bit 2 = PMC START (do collision detection when 0->1) // bit 2 = PMC START (do collision detection when 0->1)
if ((data & 4) && !(m_1f98_latch & 4)) if ((data & 4) && !(m_1f98_latch & 4))
{ {
calculate_collisions(); pmc_run();
// 100 cycle delay is arbitrary
m_thunderx_firq_timer->adjust(m_maincpu->cycles_to_attotime(100));
} }
scontra_1f98_w(data); scontra_1f98_w(data);