summaryrefslogtreecommitdiffstatshomepage
path: root/src/mame/machine/znsec.c
blob: 77172aa9a2adbef0948f6c0014a904c223e5fc14 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
/*

  CAT702 ZN security chip

  A serial magic latch.

  It's a DIP20 chip with a sticker of the form XXnn, where XX is the
  company and nn a number:
    AC = Acclaim
    AT = Atlus
    CP = Capcom
    ET = Raizing
    KN = Konami
    MG = Tecmo
    TT = Taito
    TW = Atari

  There usually are 2 of them, one on the cpu board and one on the rom
  board.  The cpu board one is usually numbered 01.

  Pinout:             GND -11  10- GND
                        ? -12   9- +5V
                      +5V -13   8- Data in
                 Data out- 14   7- Clock
                      +5V -15   6- Select
                        ? -16   5- Select
                      +5V -17   4- +5V
                      +5V -18   3- +5V
                      +5V -19   2- +5V
                      +5V -20   1- ?

  The chip works with the '?' lines left unconnected.

  The communication protocol is serial, and in practice the standard
  psx controller communication protocol minus the ack.  Drive both
  select to ground to start a communication, send bits and get the
  results on the raising clock.  Put both select back to +5V when
  finished.  The bios seems to use two communication clock speeds,
  ~300KHz (standard psx) and ~2MHz.  Driving it with lower clocks
  works reasonably, at least at 1KHz.

  The data is divided in bytes but there is no signal for end-of-byte.
  In all of the following the data will be considered coming and going
  lower-bit first.

  Internally the chip has a 8-bit state, initialized at communication
  start to 0xfc.  The structure is simple:


                  +---------+         bit number        +--------+
  Clock   ------->| bit     |-----+-------------------->| bit    |---------> Data out
                  | counter |     |                     | select |
                  +---------+     v      +-------+ out  |        |
                      |        +-----+   | 8bit  |=====>|        |
  Data in ------------|------->| TF1 |<=>| state |      +--------+
                      |        +-----+   |       |
                      |                  |       |
                      | start  +-----+   |       |
                      +------->| TF2 |<=>|       |
                               +-----+   +-------+

  The chip starts by tranforming the state with TF2.  Then, for each
  input bit from 0 to 7:
    - the nth bit from the state is sent to the output
    - the state is transformed by TF1 if the input bit is 0

  TF2 is a fixed linear substitution box (* = and, + = xor):
    o = ff*s0 + fe*s1 + fc*s2 + f8*s3 + f0*s4 + e0*s5 + c0*s6 + 7f*s7

  TF1 is a chip-dependent set of 8 linear sboxes, one per bit number.
  In practice, only the sbox for bit 0 is defined for the chip, the 7
  other are derived from it.  Defining the byte transformation Shift
  as:
       Shift(i7..i0) = i6..i0, i7^i6

  and noting the sboxes as:
       Sbox(n, i7..i0) =    Xor(    c[n, bit]*i[bit])
                         0<=bit<=7
  then
       c[n, bit=0..6] = Shift(c[n-1, (bit-1)&7])
       c[n, 7]        = Shift(c[n-1, 6])^c[n, 0]
                      = Shift(c[n-1, 6])^Shift(c[n-1, 7])
*/

#include "driver.h"
#include "znsec.h"

typedef struct {
	const UINT8 *transform;
	UINT8 state;
	UINT8 bit;
} znsec_state;

static znsec_state zns[2];


// Given the value for x7..x0 and linear transform coefficients a7..a0
// compute the value of the transform
#if 0
static int c_linear(UINT8 x, UINT8 a)
{
	int i;
	UINT8 r;
	x &= a;
	r = 0;
	for(i=0; i<8; i++)
		if(x & (1<<i))
			r = !r;
	return r;
}
#endif

// Derive the sbox xor mask for a given input and select bit
static UINT8 compute_sbox_coef(int chip, int sel, int bit)
{
	UINT8 r;
	if(!sel)
		return zns[chip].transform[bit];
	r = compute_sbox_coef(chip, (sel-1) & 7, (bit-1) & 7);
	r = (r << 1)|(((r >> 7)^(r >> 6)) & 1);
	if(bit != 7)
		return r;

	return r ^ compute_sbox_coef(chip, sel, 0);
}

// Apply the sbox for a input 0 bit
static UINT8 apply_bit_sbox(int chip, UINT8 state, int sel)
{
	int i;
	UINT8 r = 0;
	for(i=0; i<8; i++)
		if(state & (1<<i))
			r ^= compute_sbox_coef(chip, sel, i);
	return r;
}

// Apply a sbox
static UINT8 apply_sbox(UINT8 state, const UINT8 *sbox)
{
	int i;
	UINT8 r = 0;
	for(i=0; i<8; i++)
		if(state & (1<<i))
			r ^= sbox[i];
	return r;
}

void znsec_init(int chip, const UINT8 *transform)
{
	zns[chip].transform = transform;
	zns[chip].state = 0xfc;
	zns[chip].bit = 0;
}

void znsec_start(int chip)
{
	zns[chip].state = 0xfc;
	zns[chip].bit = 0;
}

UINT8 znsec_step(int chip, UINT8 input)
{
	UINT8 res;
	static const UINT8 initial_sbox[8] = { 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x7f };

	if (zns[chip].bit==0)
	{
		// Apply the initial xbox
		zns[chip].state = apply_sbox(zns[chip].state, initial_sbox);
	}

	// Compute the output and change the state
	res = (zns[chip].state>>zns[chip].bit) & 1;
	if((input & 1)==0)
		zns[chip].state = apply_bit_sbox(chip, zns[chip].state, zns[chip].bit);

	zns[chip].bit++;
	zns[chip].bit&=7;
	return res;
}