SNES DevelopmentA DSP chip from Capcom; it is actually a Hitachi HG51B169 as confirmed by decapping. There are 1024K words of 24-bit instructions, running as 20.000MHz. and it is used in 2 games:
The test program is located at $029C00.
The CX4 has 16 multi-purpose triple-byte registers in the memory range $7F80 through $7FAF.
| Mode | Size | Bank | RAM | Registers |
|---|---|---|---|---|
| 0x20H | 2Mbit - 32Mbit | $00 - $3F | $6000 - $6BFF | $7F40 - $7FAF |
| Registers | Function |
|---|---|
$7F40 - $7F47 | DMA Transfer (?) |
$7F49 - $7F4B | ROM Offset |
$7F4D - $7F4E | Page Select |
$7F4F | Instruction Pointer |
Start Address = ((Page_Select * 256) + Instruction Pointer) * 2) + ROM_Offset
I ran some tests on a Mashmods flash programmer and MMX2 cart and these two cases give the same results.
Page Select = $000E and ROM_Offset = $028000
Page Select = $010E and ROM_Offset = $008000
Program ROM is obviously 256x16-bit pages at a time (taken from the SNES ROM). Program RAM is 2x256x16-bit (two banks). Data ROM is 1024x24-bit (only ROM internal to the Cx4). Data RAM is 4x384x16-bit. Call stack is 8-levels deep, at least 16-bits wide.
| Memory Type | Size | Location |
|---|---|---|
| Program ROM | 256 x 16-bit | SNES ROM |
| Program RAM | 2 x 256 x 16-bit (2 Banks) | |
| Data ROM | 1024 x 24-bit | CX4 Internal |
| Data RAM | 4 x 384 x 16-bit |
| Location | Table Data |
|---|---|
| 000 - 0FF | Inverse |
| 100 - 1FF | Square Root (sqrt) |
| 200 - 27F | First Quadrant Sine (sin) |
| 280 - 2FF | First Quadrant Arcsine (asin) |
| 300 - 37F | First Quadrant Tangent (tan) |
| 380 - 3FF | First Quadrant Cosine (cos) |
Commands are executed on the CX4 by writing the command to $7F4F while bit 6 of $7F5E is clear. Bit 6 of $7F5E will stay set until the command has completed, at which time output data will be available. See individual commands for input and output parameter addresses.
| Command | Function Name |
|---|---|
$00 | Sprite Functions |
$01 | Wireframe |
$05 | Propulsion |
$0D | Set Vector Length |
$10 | Triangle |
$13 | Triangle |
$15 | Pythagorean |
$1F | Arc-Tan |
$22 | Trapezoid |
$25 | Multiply |
$2D | Transform Coordinates |
Test Functions
| Command | Function Name |
|---|---|
$00 - $3F | Command Shift |
$40 | Sum |
$54 | Square |
$5C | Immediate Register |
$5E - $7E | Immediate Register (Multiple) |
$89 | Immediate ROM |
The following steps must be completed when accessing these functions:
$xx7F5e is clear$0E to $xx7F4D$00 to $xx7F4E$01 to $xx7F48$xx7F5E is clear$xx7F4F$xx7F5E is clearReversed engineered by zsknight and documented by anomie. Source code and additional information by Overload.
unsigned char CX4_Ram[0x0C00];
unsigned char CX4_Reg[0x0100];
#define uint24 unsigned int
// 24 Bit Work Registers
uint24 R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, R13, R14, R15;
const uint24 CX4_SinTable[256] = {
0x000000, 0x000324, 0x000648, 0x00096c, 0x000c8f, 0x000fb2, 0x0012d5, 0x0015f6,
0x001917, 0x001c37, 0x001f56, 0x002273, 0x002590, 0x0028aa, 0x002bc4, 0x002edb,
0x0031f1, 0x003505, 0x003817, 0x003b26, 0x003e33, 0x00413e, 0x004447, 0x00474d,
0x004a50, 0x004d50, 0x00504d, 0x005347, 0x00563e, 0x005931, 0x005c22, 0x005f0e,
0x0061f7, 0x0064dc, 0x0067bd, 0x006a9b, 0x006d74, 0x007049, 0x007319, 0x0075e5,
0x0078ad, 0x007b70, 0x007e2e, 0x0080e7, 0x00839c, 0x00864b, 0x0088f5, 0x008b9a,
0x008e39, 0x0090d3, 0x009368, 0x0095f6, 0x00987f, 0x009b02, 0x009d7f, 0x009ff6,
0x00a267, 0x00a4d2, 0x00a736, 0x00a994, 0x00abeb, 0x00ae3b, 0x00b085, 0x00b2c8,
0x00b504, 0x00b73a, 0x00b968, 0x00bb8f, 0x00bdae, 0x00bfc7, 0x00c1d8, 0x00c3e2,
0x00c5e4, 0x00c7de, 0x00c9d1, 0x00cbbb, 0x00cd9f, 0x00cf7a, 0x00d14d, 0x00d318,
0x00d4db, 0x00d695, 0x00d848, 0x00d9f2, 0x00db94, 0x00dd2d, 0x00debe, 0x00e046,
0x00e1c5, 0x00e33c, 0x00e4aa, 0x00e60f, 0x00e76b, 0x00e8bf, 0x00ea09, 0x00eb4b,
0x00ec83, 0x00edb2, 0x00eed8, 0x00eff5, 0x00f109, 0x00f213, 0x00f314, 0x00f40b,
0x00f4fa, 0x00f5de, 0x00f6ba, 0x00f78b, 0x00f853, 0x00f912, 0x00f9c7, 0x00fa73,
0x00fb14, 0x00fbac, 0x00fc3b, 0x00fcbf, 0x00fd3a, 0x00fdab, 0x00fe13, 0x00fe70,
0x00fec4, 0x00ff0e, 0x00ff4e, 0x00ff84, 0x00ffb1, 0x00ffd3, 0x00ffec, 0x00fffb,
0x000000, 0xfffcdb, 0xfff9b7, 0xfff693, 0xfff370, 0xfff04d, 0xffed2a, 0xffea09,
0xffe6e8, 0xffe3c8, 0xffe0a9, 0xffdd8c, 0xffda6f, 0xffd755, 0xffd43b, 0xffd124,
0xffce0e, 0xffcafa, 0xffc7e8, 0xffc4d9, 0xffc1cc, 0xffbec1, 0xffbbb8, 0xffb8b2,
0xffb5af, 0xffb2af, 0xffafb2, 0xffacb8, 0xffa9c1, 0xffa6ce, 0xffa3dd, 0xffa0f1,
0xff9e08, 0xff9b23, 0xff9842, 0xff9564, 0xff928b, 0xff8fb6, 0xff8ce6, 0xff8a1a,
0xff8752, 0xff848f, 0xff81d1, 0xff7f18, 0xff7c63, 0xff79b4, 0xff770a, 0xff7465,
0xff71c6, 0xff6f2c, 0xff6c97, 0xff6a09, 0xff6780, 0xff64fd, 0xff6280, 0xff6009,
0xff5d98, 0xff5b2d, 0xff58c9, 0xff566b, 0xff5414, 0xff51c4, 0xff4f7a, 0xff4d37,
0xff4afb, 0xff48c5, 0xff4697, 0xff4470, 0xff4251, 0xff4038, 0xff3e27, 0xff3c1e,
0xff3a1b, 0xff3821, 0xff362e, 0xff3444, 0xff3260, 0xff3085, 0xff2eb2, 0xff2ce7,
0xff2b24, 0xff296a, 0xff27b7, 0xff260d, 0xff246b, 0xff22d2, 0xff2141, 0xff1fb9,
0xff1e3a, 0xff1cc3, 0xff1b55, 0xff19f0, 0xff1894, 0xff1740, 0xff15f6, 0xff14b4,
0xff137c, 0xff124d, 0xff1127, 0xff100a, 0xff0ef6, 0xff0dec, 0xff0ceb, 0xff0bf4,
0xff0b05, 0xff0a21, 0xff0945, 0xff0874, 0xff07ac, 0xff06ed, 0xff0638, 0xff058d,
0xff04eb, 0xff0453, 0xff03c4, 0xff0340, 0xff02c5, 0xff0254, 0xff01ec, 0xff018f,
0xff013b, 0xff00f1, 0xff00b1, 0xff007b, 0xff004e, 0xff002c, 0xff0013, 0xff0004
}
The resolution of an Angle is 9 bits, 8 bits data plus a sign bit. Angles range from -180° ~ +180°.
uint24 CX4_Sin(uint24 Rx){
R0 = Rx & 0x1ff;
if (R0 & 0x100) R0 ^= 0x1ff;
if (R0 & 0x080) R0 ^= 0x0ff;
if (Rx & 0x100)
return CX4_SinTable[R0 + 0x80];
else
return CX4_SinTable[R0];
}
uint24 CX4_Cos(uint24 Rx){
return CX4_Sin(Rx + 0x080);
}
bool CX4_Adc24(uint24 &A, uint24 B, bool Carry){
uint32 C = (A & 0xffffff) + (B & 0xffffff);
if (Carry) C++;
A = (uint24) C;
return (C & 0x1000000);
}
void CX4_Mul24(uint24 A, uint24 B, uint24 &CL, uint24 &CH){
if (B & 0x800000){
A = -A;
B = -B;
}
CL = CH = 0;
uint24 AdderL = A;
uint24 AdderH = 0;
if (A & 0x800000) AdderH--;
B &= 0xffffff;
while (B){
if (B & 1)
CX4_Adc24(CH, AdderH, CX4_Adc24(CL, AdderL, false));
AdderH <<= 1;
if (AdderL & 0x800000) AdderH++;
AdderL <<= 1;
B >>= 1;
}
}
uint24 CX4_Ldr24(uint32 ofs){
return (CX4_Reg[ofs + 0] | (CX4_Reg[ofs + 1] << 8) | (CX4_Reg[ofs + 2] << 16));
}
void CX4_Str24(uint32 ofs, uint24 Rx){
CX4_Reg[ofs + 0] = (uint8) (Rx);
CX4_Reg[ofs + 1] = (uint8) (Rx >> 8);
CX4_Reg[ofs + 2] = (uint8) (Rx >> 16);
}
N/A
N/A
N/A
| I/O | Address | Name / Variable |
|---|---|---|
| Input | $7F80 - $7F82 | Angle (R0) |
$7F83 - $7F85 | Radius (R1) | |
| Output | $7F86 - $7F88 | X (R2) |
$7F89 - $7F8B | Y (R3) |
void CX4_Triangle16(){
R0 = CX4_Ldr24(0x80);
R1 = CX4_Ldr24(0x83);
R4 = R0 & 0x1ff;
if (R1 & 0x8000) R1 |= 0xff0000;
CX4_Mul24(CX4_Cos(R4), R1, R5, R2);
R5 = (R5 >> 16) & 0xff;
R2 = (R2 << 8) + R5;
CX4_Mul24(CX4_Sin(R4), R1, R5, R3);
R5 = (R5 >> 16) & 0xff;
R3 = (R3 << 8) + R5;
CX4_Str24(0x80, R0);
CX4_Str24(0x83, R1);
CX4_Str24(0x86, R2);
CX4_Str24(0x89, R3);
CX4_Str24(0x8c, R4);
CX4_Str24(0x8f, R5);
}
| I/O | Address | Name / Variable |
|---|---|---|
| Input | $7F80 - $7F82 | Angle (R0) |
$7F83 - $7F85 | Radius (R1) | |
| Output | $7F86 - $7F88 | X (R2) |
$7F89 - $7F8B | Y (R3) |
void CX4_Triangle24(){
R0 = CX4_Ldr24(0x80);
R1 = CX4_Ldr24(0x83);
R4 = R0 & 0x1ff;
CX4_Mul24(CX4_Cos(R4), R1, R5, R2);
R5 = (R5 >> 8) & 0xffff;
R2 = (R2 << 16) + R5;
CX4_Mul24(CX4_Sin(R4), R1, R5, R3);
R5 = (R5 >> 8) & 0xffff;
R3 = (R3 << 16) + R5;
CX4_Str24(0x80, R0);
CX4_Str24(0x83, R1);
CX4_Str24(0x86, R2);
CX4_Str24(0x89, R3);
CX4_Str24(0x8c, R4);
CX4_Str24(0x8f, R5);
}
N/A
N/A
N/A
| I/O | Address | Name / Variable |
|---|---|---|
| Input | $7F80 - $7F82 | Multiplicand (R0) |
$7F83 - $7F85 | Multiplier (R1) | |
| Output | $7F80 - $7F85 | Product (R1:R0) |
void CX4_Multiply(){
R0 = CX4_Ldr24(0x80);
R1 = CX4_Ldr24(0x83);
CX4_Mul24(R0, R1, R0, R1);
CX4_Str24(0x80, R0);
CX4_Str24(0x83, R1);
}
N/A
| I/O | Address | Name / Variable |
|---|---|---|
| Input | $6000 - $67FF | |
| Output | $7F80 - $7F82 | (R0) |
void CX4_Sum(){
R0 = 0;
for (uint32 i = 0; i < 0x800; i++) R0 += CX4_Ram[i];
CX4_Str24(0x80, R0);
}
| I/O | Address | Name / Variable |
|---|---|---|
| Input | $7F80 - $7F82 | (R0) |
| Output | $7F83 - $7F88 | (R2:R1) |
void CX4_Square(){
R0 = CX4_Ldr24(0x80);
CX4_Mul24(R0, R0, R1, R2);
CX4_Str24(0x83, R1);
CX4_Str24(0x86, R2);
}
| I/O | Address | Name / Variable |
|---|---|---|
| Input | None | |
| Output | $6000 - $6030 | |
$7F80 - $7F82 | (R0) |
const unsigned char ImmediateData[48] = {
0x00, 0xfe, 0xff, 0x00, 0x01, 0x00, 0xfe, 0xff, 0xff, 0x01, 0x00, 0x00,
0xff, 0xff, 0x7f, 0xff, 0x7f, 0xff, 0x00, 0x7f, 0xff, 0x00, 0x80, 0x00,
0x7f, 0xff, 0xff, 0x80, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, 0xff, 0xff,
0xff, 0x00, 0x00, 0x00, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00
};
void CX4_ImmediateReg(){
R0 = 0;
for (uint32 i = 48; i > 0; --i){
CX4_Ram[R0] = ImmediateData[i];
R0++;
}
CX4_Str24(0x80, R0);
}
| I/O | Address | Name / Variable |
|---|---|---|
| Input | $7F80 - $7F82 | (R0) |
| Output | $6XXX - $6XXX | |
$7F80 - $7F82 | (R0) |
This command transfers a preset triple-byte pattern into the memory offset specified in R0.
| Command | Number of Bytes |
|---|---|
$5E | 48 |
$60 | 45 |
$62 | 42 |
$64 | 39 |
$66 | 36 |
$68 | 33 |
$6A | 30 |
$6C | 27 |
$6E | 24 |
$70 | 21 |
$72 | 18 |
$74 | 15 |
$76 | 12 |
$78 | 9 |
$7A | 6 |
$7C | 3 |
void CX4_ImmediateReg(uint32 NumberOfBytes){
R0 = CX4_Ldr24(0x80);
for (uint32 i = NumberOfBytes; i > 0; --i){
CX4_Ram[R0 & 0xfff] = ImmediateData[i];
R0++;
}
CX4_Str24(0x80, R0);
}
| I/O | Address | Name / Variable |
|---|---|---|
| Input | None | |
| Output | $7F80 - $7F85 | (R1:R0) |
void CX4_ImmediateROM(){
R0 = 0x054336;
R1 = 0xffffff;
CX4_Str24(0x80, R0);
CX4_Str24(0x83, R1);
}
ROM_Offset = 028000
Page Select = 0006
PC = 04
04: 606b lda r11
05: 7000 sta rp
06: 6008 lda rom
07: e06c sta r12
08: 3c00 ret
5740404, 5513374, 5426600, 5440747, 5535417, 5381360, 5832258
Information provided by Overload (codeviolation@hotmail.com) / Dr. Decapitator / byuu. Jonas Quinn - Program ROM discovery. Overload - Data ROM extraction. Segher - Instruction set reverse-engineering.