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
S.P.Y. In this game, however, the only thing it seems to do is clear the
screen.
This is the program for the 052591:
00: 5f 80 01 e0 08
01: df 80 00 e0 0c
02: df 90 02 e0 0c
03: df a0 03 e0 0c
04: df b0 0f e0 0c
05: df c0 ff bf 0c
06: 5c 02 00 33 0c
07: 5f 80 04 80 0c
This is the 052591 PMC code loaded at startup, it contains a RAM/VRAM filling program.
See https://github.com/furrtek/SiliconRE/tree/master/Konami/052591 for details
00: 5f 80 01 e0 08 Entry point, set OUT0 high
01: df 80 00 e0 0c r0 = 0
02: df 90 02 e0 0c r1 = 2
03: df a0 03 e0 0c r2 = 3
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
09: df 70 00 cb 08
0a: 5f 80 00 80 0c
09: df 70 00 cb 08 r7 = RAM(4)
0a: 5f 80 00 80 0c ExtAddr = 0
0b: 5c 04 00 2b 0c
0c: df 60 00 cb 08
0d: 5c 0c 1f e9 0c
0e: 4c 0c 2d e9 08
0f: 5f 80 03 80 0c
0c: df 60 00 cb 08 r6 = RAM(0) (commands 0, 1 and 30 are used)
0d: 5c 0c 1f e9 0c JP 1F if r6 == 0
0e: 4c 0c 2d e9 08 JP 2D if r6 == 1
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
11: 5f 00 00 cb 00
12: 5f 80 02 a0 0c
13: df d0 00 c0 04
14: 01 3a 00 f3 0a
11: 5f 00 00 cb 00 Read MSB from RAM[3]
12: 5f 80 02 a0 0c ExtAddr = 2
13: df d0 00 c0 04 r5.w = RAM[3], RAM[2]
14: 01 3a 00 f3 0a acc = r5 + r3 = r5 + f
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
18: 5c 00 00 13 0c
18: 5c 00 00 13 0c Write r7 to RAM[0]
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
1c: 48 0a 5b d1 0c
1d: 5f 80 00 e0 08
1e: 5f 00 1e fd 0c
1c: 48 0a 5b d1 0c Write 0 to RAM[r5++] until r5 > acc (16 times)
1d: 5f 80 00 e0 08 Set OUT0 low
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
20: df 20 00 cb 08
20: df 20 00 cb 08 r2 = RAM[1]
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
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
26: 5c 08 00 93 0c
27: 9f 91 ff cf 0e
26: 5c 08 00 93 0c Write 0 to RAM[0]
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
29: 84 00 00 b3 0c
2a: 49 10 69 d1 0c
2b: 5f 80 00 e0 08
2c: 5f 00 2c fd 0c
29: 84 00 00 b3 0c ExtAddr = r0
2a: 49 10 69 d1 0c Write r2 to RAM[r0++] while r0 < r1
2b: 5f 80 00 e0 08 Set OUT0 low
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
2e: df 20 00 cb 08
2e: df 20 00 cb 08 r2 = RAM(1)
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
32: 5f 80 01 00 0c
32: 5f 80 01 00 0c Write 3 to RAM[0]
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
36: 5c 08 00 93 0c
37: 9f 91 ff cf 0e
36: 5c 08 00 93 0c Write 0 to RAM[0]
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
39: 84 00 00 b3 0c
3a: 49 10 79 d1 0c
3b: 5f 80 00 e0 08
3c: 5f 00 3c fd 0c
3d: ff ff ff ff ff
3e: ff ff ff ff ff
3f: ff ff ff ff ff
3a: 49 10 79 d1 0c Write r2 to RAM[r0++] while r0 < r1
3b: 5f 80 00 e0 08 Set OUT0 low
3c: 5f 00 3c fd 0c JP 3C, infinite loop
3d: ff ff ff ff ff Garbage
3e: ff ff ff ff ff Garbage
3f: ff ff ff ff ff Garbage
***************************************************************************/
@ -92,6 +102,7 @@ Notes:
#include "emupal.h"
#include "speaker.h"
#include "tilemap.h"
#include "multibyte.h"
// configurable logging
@ -120,7 +131,7 @@ public:
m_gfxdecode(*this, "gfxdecode"),
m_palette(*this, "palette"),
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_tilesrom(*this, "tiles")
{ }
@ -138,7 +149,7 @@ private:
required_device<palette_device> m_palette;
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_region_ptr<uint8_t> m_tilesrom;
@ -231,12 +242,21 @@ void hexion_state::bankswitch_w(uint8_t data)
// bits 0-3 select ROM bank
m_rombank->set_entry(data & 0x0f);
// does bit 6 trigger the 052591?
// bit 6 triggers the 052591
if (data & 0x40)
{
int bank = m_unkram[0] & 1;
memset(m_vram[bank], m_unkram[1], 0x2000);
m_bg_tilemap[bank]->mark_all_dirty();
uint8_t command = m_pmcram[0];
if (command <= 1)
{
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
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)
{
return m_unkram[offset];
return m_pmcram[offset];
}
else
{
@ -289,8 +309,8 @@ void hexion_state::bankedram_w(offs_t offset, uint8_t data)
{
if (m_pmcbank)
{
LOGBANKEDRAM("%s: unkram_w offset %04x, data %02x, bankctrl = %02x\n", m_maincpu->pc(), offset, data, m_bankctrl);
m_unkram[offset] = data;
LOGBANKEDRAM("%s: pmcram_w offset %04x, data %02x, bankctrl = %02x\n", m_maincpu->pc(), offset, data, m_bankctrl);
m_pmcram[offset] = data;
}
else
LOGBANKEDRAM("%04x pmc internal ram %04x = %02x\n", m_maincpu->pc(), offset, data);

View File

@ -11,6 +11,9 @@
Revisions:
31-01-2024 Furrtek
- updated PMCU collision check code to match what the original program does
05-10-2002 Acho A. Tang
- simulated PMCU protection(guess only)
- changed priority scheme to fix graphics in 3D levels
@ -34,6 +37,7 @@
#include "sound/ymopl.h"
#include "emupal.h"
#include "speaker.h"
#include "multibyte.h"
namespace {
@ -63,6 +67,7 @@ private:
/* misc */
int m_rambank = 0;
int m_pmcbank = 0;
uint8_t m_pmcpc = 0;
int m_video_enable = 0;
int m_old_3f90 = 0;
@ -85,7 +90,7 @@ private:
virtual void machine_start() override;
virtual void machine_reset() override;
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_callback1(uint8_t data);
K052109_CB_MEMBER(tile_callback);
@ -172,8 +177,7 @@ uint8_t spy_state::spy_bankedram1_r(offs_t offset)
}
else
{
//logerror("%04x read pmc internal ram %04x\n", m_maincpu->pc(), offset);
return 0;
return 0; // PMC internal RAM can't be read back
}
}
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);
m_pmcram[offset] = data;
}
//else
//logerror("%04x pmc internal ram %04x = %02x\n", m_maincpu->pc(), offset, data);
else
{
// Set initial PMC PC
m_pmcpc = data & 0x3f;
}
}
else
m_ram[offset] = data;
}
/*
this is the data written to internal ram on startup:
00: e7 7e 38 fc 08
01: df 36 38 dc 00
02: df 12 3a dc 00
03: df 00 38 dc 08
This is the 052591 PMC code loaded at startup, it contains both projection and collision check programs.
See https://github.com/furrtek/SiliconRE/tree/master/Konami/052591 for details
Coordinate projection routine:
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
05: 26 fe 00 ff 0c
06: 89 03 34 fc 0d
07: 81 03 34 fc 09
08: 81 03 34 fc 09
09: 81 03 34 fc 09
0a: 81 03 2f fc 09
0b: cc 36 0e d9 08
05: 26 fe 00 ff 0c Acc.w = RAM[5],00
06: 89 03 34 fc 0d 1+16 division steps, CALL 34...
07: 81 03 34 fc 09 add/sub, CALL 34
08: 81 03 34 fc 09 add/sub, CALL 34
09: 81 03 34 fc 09 add/sub, CALL 34
0a: 81 03 2f fc 09 add/sub, CALL 34
0b: cc 36 0e d9 08 r3--, JP 0e if 0
0c: 84 7e 00 ab 0c
0d: 5f 7e 03 cd 08
0e: 7f 80 fe ef 08
0f: 5f 7e 0f fd 08
10: e7 7e 38 fc 08
0d: 5f 7e 03 cd 08 JP 03
0e: 7f 80 fe ef 08 Set OUT0 low
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
12: df 12 0e d9 08
13: df ec 10 e0 0c
14: 1f fe 03 e0 0c
15: df fe 03 e0 0c
16: dc 5e 3e fc 08
17: df 12 2b d9 08
18: 67 25 38 fc 0c
12: df 12 0e d9 08 r1 = RAM[2], JP 0e if 0
13: df ec 10 e0 0c r6 = 10
14: 1f fe 03 e0 0c Acc = 3
15: df fe 03 e0 0c r7 = 3
16: dc 5e 3e fc 08 r5 = 3
17: df 12 2b d9 08 r1 = RAM[r6++], JP 2b if 0
18: 67 25 38 fc 0c r2 = 8000 ?
19: df 12 3c dc 00
1a: df 36 00 db 00
1b: c1 14 00 fb 08
1c: c1 34 38 fc 08
1b: c1 14 00 fb 08 r1 += r2
1c: c1 34 38 fc 08 r3 += r2
1d: c5 22 37 dc 00
1e: cd 12 3c dc 04
1f: c5 46 3b dc 00
20: cd 36 00 db 04
21: 49 16 ed f9 0c
22: c9 18 ea f9 0c
21: 49 16 ed f9 0c JP 2d if r1 < r3
22: c9 18 ea f9 0c JP 2a if r1 < r4
23: dc 12 2a f9 08
24: cc 5a 26 f9 08
25: 5f 7e 18 fd 08
26: 5a 7e 32 f8 08
27: 84 6c 33 9c 0c
28: cc 00 0e d9 08
24: cc 5a 26 f9 08 r5--, JP 26 if 0
25: 5f 7e 18 fd 08 JP 18
26: 5a 7e 32 f8 08 Clear RAM(r7++) if Acc is 0
27: 84 6c 33 9c 0c RAM(r6++) = Acc
28: cc 00 0e d9 08 r0--, JP 0e if 0
29: 5f 7e 14 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
2d: dc 16 00 fb 08
2e: dc 44 22 fd 08
2f: cd fe 02 e0 0c
2d: dc 16 00 fb 08 r1 = r3
2e: dc 44 22 fd 08 r4 = r2
2f: cd fe 02 e0 0c r7 -= 3
30: 84 7e 00 bb 0c
31: 5a 7e 00 73 08
32: 84 7e 00 9b 0c
33: 5a 7e 00 36 08
34: 81 03 00 fb 09
35: 81 03 00 fb 09
36: 81 03 00 fe 09
37: cd fe 01 e0 0c
34: 81 03 00 fb 09 add/sub
35: 81 03 00 fb 09 add/sub
36: 81 03 00 fe 09 add/sub, ret
37: cd fe 01 e0 0c r7 -= 2
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
3b: cd ec 01 e0 0c
3b: cd ec 01 e0 0c r6 -= 2
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
3f: 5f 7e 00 ce 08
3f: 5f 7e 00 ce 08 ret
*/
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);
}
void spy_state::spy_collision( )
void spy_state::pmc_run( )
{
#define MAX_SPRITES 64
#define DEF_NEAR_PLANE 0x6400
#define NEAR_PLANE_ZOOM 0x0100
#define FAR_PLANE_ZOOM 0x0000
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)
constexpr uint16_t MAX_SPRITES = 64;
constexpr uint16_t DEF_NEAR_PLANE = 0x6400;
if (m_pmcpc == 0x00)
{
x1 = (m_pmcram[0x3] << 8) + m_pmcram[0x4];
w1 = (m_pmcram[0x5] << 8) + m_pmcram[0x6];
z1 = (m_pmcram[0x7] << 8) + m_pmcram[0x8];
d1 = (m_pmcram[0x9] << 8) + m_pmcram[0xa];
y1 = (m_pmcram[0xb] << 8) + m_pmcram[0xc];
h1 = (m_pmcram[0xd] << 8) + m_pmcram[0xe];
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];
// Projection program
// Basically divides a list of 16-bit words by a constant, results are 8.8 fixed point
uint16_t loopend, nearplane;
uint32_t op;
loopend = get_u16be(&m_pmcram[0]);
nearplane = get_u16be(&m_pmcram[2]);
// fail safe
if (loopend > MAX_SPRITES)
@ -354,16 +323,65 @@ void spy_state::spy_collision( )
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];
op2 = (op2 * (NEAR_PLANE_ZOOM - FAR_PLANE_ZOOM)) / nearplane + FAR_PLANE_ZOOM;
m_pmcram[i] = op2 >> 8;
m_pmcram[i + 1] = op2 & 0xff;
op = get_u16be(&m_pmcram[i]);
op = (op << 8) / nearplane;
put_u16be(&m_pmcram[i], op);
}
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 */
m_pmcbank = (data & 0x80) >> 7;
//logerror("%04x: 3f90_w %02x\n", m_maincpu->pc(), data);
/* bit 6 = PMC-START */
if ((data & 0x40) && !(m_old_3f90 & 0x40))
{
/* we should handle collision here */
//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
pmc_run();
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_rambank));
save_item(NAME(m_pmcbank));
save_item(NAME(m_pmcpc));
save_item(NAME(m_video_enable));
save_item(NAME(m_old_3f90));
save_item(NAME(m_pmcram));
@ -610,6 +613,7 @@ void spy_state::machine_reset()
{
m_rambank = 0;
m_pmcbank = 0;
m_pmcpc = 0;
m_video_enable = 0;
m_old_3f90 = -1;
}

View File

@ -49,6 +49,7 @@
#include "emupal.h"
#include "screen.h"
#include "speaker.h"
#include "multibyte.h"
//#define VERBOSE 1
#include "logmacro.h"
@ -164,8 +165,8 @@ private:
void thunderx_videobank_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 calculate_collisions();
//void run_collisions(int s0, int e0, int s1, int e1, int cm, int hm);
void pmc_run();
void thunderx_map(address_map &map) ATTR_COLD;
};
@ -258,8 +259,7 @@ uint8_t thunderx_state::pmc_r(offs_t offset)
}
else
{
LOG("%04x read pmc internal ram %04x\n", m_audiocpu->pc(), offset);
return 0;
return 0; // PMC internal RAM can't be read back
}
}
@ -272,141 +272,86 @@ void thunderx_state::pmc_w(offs_t offset, uint8_t data)
}
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
00: e7 00 00 ad 08 e7 00 00 ad 08
01: 5f 80 05 a0 0c 1f 80 05 a0 0c LDW ACC,RAM+05
02: 42 7e 00 8b 04 regE
03: df 00 e2 8b 08 df 8e 00 cb 04 regE
04: 5f 80 06 a0 0c 5f 80 07 a0 0c LDB ACC,RAM+07
05: df 7e 00 cb 08 df 7e 00 cb 08 LDB R7,[Rx]
06: 1b 80 00 a0 0c 1b 80 00 a0 0c LDPTR #0 PTR2,RAM+00
07: df 10 00 cb 08 df 10 00 cb 08 LDB R1,[PTR] (fl) LDB R1,[Rx] (flags)
08: 5f 80 03 a0 0c 5f 80 03 a0 0c LDB R0,[3] (cm) LDB ACC,RAM+03 load collide mask
09: 1f 20 00 cb 08 1f 20 00 cb 08 LD CMP2,R0 test (AND) R1 vs ACC
0a: c4 00 00 ab 0c c4 00 00 ab 0c INC PTR LEA Rx,[++PTR2]
0b: df 20 00 cb 08 df 20 00 cb 08 LDB R2,[PTR] (w) LDB R2,[Rx] (width)
0c: c4 00 00 ab 0c c4 00 00 ab 0c INC PTR LEA Rx,[++PTR2]
0d: df 30 00 cb 08 df 30 00 cb 08 LDB R3,[PTR] (h) LDB R3,[Rx] (height)
0e: c4 00 00 ab 0c c4 00 00 ab 0c INC PTR LEA Rx,[++PTR2]
0f: df 40 00 cb 08 df 40 00 cb 08 LDB R4,[PTR] (x) LDB R4,[Rx] (x)
10: c4 00 00 ab 0c c4 00 00 ab 0c INC PTR LEA Rx,[++PTR2]
11: df 50 00 cb 08 df 50 00 cb 08 LDB R5,[PTR] (y) LDB R5,[Rx] (y)
12: 60 22 35 e9 08 60 22 36 e9 08 BANDZ CMP2,R1,36 R2/R1, BEQ 36
13: 44 0e 00 ab 08 44 0e 00 ab 08 MOVE PTR,INNER LEA Rx,[PTR,0] load flags
14: df 60 00 cb 08 df 60 00 cb 08 LDB R6,[PTR] (fl) LDB R6,[Rx]
15: 5f 80 04 a0 0c 5f 80 04 a0 0c LDB R0,[4] (hm) LDB ACC,RAM+04 load hit mask
16: 1f 60 00 cb 08 1f 60 00 cb 08 LD CMP6,R0 test R6 and ACC (AND)
17: 60 6c 31 e9 08 60 6c 32 e9 08 BANDZ CMP6,R6,32 R6, BEQ 32
18: 45 8e 01 a0 0c 45 8e 01 a0 0c LDB R0,[INNER+1] LDB Ry,[PTR,1] (width)
19: c5 64 00 cb 08 c5 64 00 cb 08 ADD ACC,R0,R2 R6 = ADD Ry,R2
1a: 45 8e 03 a0 0c 45 8e 03 a0 0c LDB R0,[INNER+3] LDB Ry,[PTR,3] (x)
1b: 67 00 00 cb 0c 67 00 00 cb 0c MOV CMP,R0 ??? DEC Ry
1c: 15 48 5d c9 0c 15 48 5e c9 0c SUB CMP,R4 ; BGE 1E SUB R4,Ry; Bcc 1E
1d: 12 00 00 eb 0c 12 00 00 eb 0c NEG CMP ??? NEG Ry
1e: 48 6c 71 e9 0c 48 6c 72 e9 0c B (CMP > ACC) 32 R6, BLO 32
1f: 45 8e 02 a0 0c 45 8e 02 a0 0c LDB R0,[INNER+2] LDB Ry,[PTR,2] (height)
20: c5 66 00 cb 08 c5 66 00 cb 08 ADD ACC,R0,R3 R6 = ADD Ry,R3
21: 45 8e 04 a0 0c 45 8e 04 a0 0c LDB R0,[INNER+4] LDB Ry,[PTR,4] (y)
22: 67 00 00 cb 0c 67 00 00 cb 0c MOV CMP,R0 ??? DEC Ry
23: 15 5a 64 c9 0c 15 5a 65 c9 0c SUB CMP,R5 ; BGE 25 SUB R5,Ry; Bcc 25
24: 12 00 00 eb 0c 12 00 00 eb 0c NEG CMP ??? NEG Ry
25: 48 6c 71 e9 0c 48 6c 72 e9 0c B (CMP > ACC) 32 R6, BLO 32
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
28: 5c fe 00 a0 0c 5c fe 00 a0 0c ??? STB [PTR,0]
29: df 60 00 d3 08 df 60 00 d3 08 LDB R6,
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
2c: 25 ec 04 c0 0c 25 ec 04 c0 0c STB R6,[PTR2,-4]
2d: 18 82 00 00 0c 18 82 00 00 0c
2e: 4d 80 03 a0 0c 4d 80 03 a0 0c RAM+03
2f: df e0 e6 e0 0c df e0 36 e1 0c
30: 49 60 75 f1 08 49 60 76 f1 08 Jcc 36
31: 67 00 35 cd 08 67 00 36 cd 08 Jcc 36
32: c5 fe 05 e0 0c c5 fe 05 e0 0c ADD R7,R7,5 ADD regE,#5
33: 5f 80 02 a0 0c 5f 80 02 a0 0c LDB R0, [2] LDB ACC,RAM+02
34: 1f 00 00 cb 08 1f 00 00 cb 08 LCMP CMP0,R0
35: 48 6e 52 c9 0c 48 6e 53 c9 0c BNEQ CMP0,R7, 33 R6/R7, BLO 13
36: c4 00 00 ab 0c c4 00 00 ab 0c INC PTR LEA Rx,[++PTR2]
37: 27 00 00 ab 0c 27 00 00 ab 0c
38: 42 00 00 8b 04 42 00 00 8b 04 MOVE PTR, OUTER
39: 1f 00 00 cb 00 1f 00 00 cb 00 LCMP CMP0 ?? test PTR2 vs ACC
3a: 48 00 43 c9 00 48 00 44 c9 00 BLT 4 BLT 04 next in set 0
3b: 5f fe 00 e0 08 5f fe 00 e0 08
3c: 5f 7e 00 ed 08 5f 7e 00 ed 08
3d: ff 04 00 ff 06 ff 04 00 ff 06 STOP STOP
3e: 05 07 ff 02 03 05 07 ff 02 03
3f: 01 01 e0 02 6c 01 00 60 00 a0
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 Set ext address to 5
02: 42 7e 00 8b 04
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 Set ext address to 6 or 7
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 Set ext address to r0
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 Set ext address to 3
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 r0, set ext address to r0
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 r0, set ext address to r0
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 r0, set ext address to r0
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 r0, set ext address to r0
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 JP 35 or 36 if r1 AND acc == 0
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 r6.b = RAM[r7] (flags)
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 acc.b = RAM[4] (hit mask)
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 Set ext address to r7 + 1
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 Set ext address to r7 + 3
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 JP 1D or 1E if positive
1d: 12 00 00 eb 0c 12 00 00 eb 0c NEG acc
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 Set ext address to r7 + 2
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 Set ext address to r7 + 4
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 JP 24 or 25 if positive
24: 12 00 00 eb 0c 12 00 00 eb 0c NEG acc
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
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 Set ext address to r7
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
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 acc = r6 & 4
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 Set ext address to r7 - 4
2f: df e0 e6 e0 0c df e0 36 e1 0c r6 = #$E6
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 Write acc to [r7 - 4], JP 35
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 Set ext address to 2
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 JP 12 or 13 if r7 < acc
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 Set ext address to 0
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
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 Set OUT0 low
3c: 5f 7e 00 ed 08 5f 7e 00 ed 08 JP 0
3d: ff 04 00 ff 06 ff 04 00 ff 06 Garbage
3e: 05 07 ff 02 03 05 07 ff 02 03 Garbage
3f: 01 01 e0 02 6c 01 00 60 00 a0 Garbage
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
void thunderx_state::calculate_collisions()
void thunderx_state::pmc_run()
{
// 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
//
// the operation is to intersect set 0 with set 1
// collide mask specifies 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
// masks specify objects to ignore
const int e0 = (m_pmcram[0] << 8) | m_pmcram[1];
const int e1 = m_pmcram[2];
const uint16_t e0 = get_u16be(&m_pmcram[0]);
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)
{
// US Thunder Cross uses this form
s0 = (m_pmcram[5] << 8) + m_pmcram[6];
s0 = get_u16be(&m_pmcram[5]);
s1 = m_pmcram[7];
}
else
@ -444,10 +388,44 @@ void thunderx_state::calculate_collisions()
s1 = m_pmcram[6];
}
const int cm = m_pmcram[3];
const int hm = m_pmcram[4];
const uint8_t cm = m_pmcram[3];
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)
@ -475,10 +453,7 @@ void thunderx_state::thunderx_1f98_w(uint8_t data)
// bit 2 = PMC START (do collision detection when 0->1)
if ((data & 4) && !(m_1f98_latch & 4))
{
calculate_collisions();
// 100 cycle delay is arbitrary
m_thunderx_firq_timer->adjust(m_maincpu->cycles_to_attotime(100));
pmc_run();
}
scontra_1f98_w(data);