summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib/netlist/plib/pfunction.cpp
blob: 3a1c2962169ca33517ee7f6066d2188f3f820e09 (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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
// license:GPL-2.0+
// copyright-holders:Couriersud
/*
 * palloc.c
 *
 */

#include "pfunction.h"
#include "pexception.h"
#include "pfmtlog.h"
#include "pstrutil.h"
#include "putil.h"

#include <cmath>
#include <stack>

namespace plib {

	template <typename NT>
	void pfunction<NT>::compile(const std::vector<pstring> &inputs, const pstring &expr)
	{
		if (plib::startsWith(expr, "rpn:"))
			compile_postfix(inputs, expr.substr(4));
		else
			compile_infix(inputs, expr);
	}

	template <typename NT>
	void pfunction<NT>::compile_postfix(const std::vector<pstring> &inputs, const pstring &expr)
	{
		std::vector<pstring> cmds(plib::psplit(expr, " "));
		compile_postfix(inputs, cmds, expr);
	}

	template <typename NT>
	void pfunction<NT>::compile_postfix(const std::vector<pstring> &inputs,
			const std::vector<pstring> &cmds, const pstring &expr)
	{
		m_precompiled.clear();
		int stk = 0;

		for (const pstring &cmd : cmds)
		{
			rpn_inst rc;
			if (cmd == "+")
				{ rc.m_cmd = ADD; stk -= 1; }
			else if (cmd == "-")
				{ rc.m_cmd = SUB; stk -= 1; }
			else if (cmd == "*")
				{ rc.m_cmd = MULT; stk -= 1; }
			else if (cmd == "/")
				{ rc.m_cmd = DIV; stk -= 1; }
			else if (cmd == "pow")
				{ rc.m_cmd = POW; stk -= 1; }
			else if (cmd == "sin")
				{ rc.m_cmd = SIN; stk -= 0; }
			else if (cmd == "cos")
				{ rc.m_cmd = COS; stk -= 0; }
			else if (cmd == "trunc")
				{ rc.m_cmd = TRUNC; stk -= 0; }
			else if (cmd == "rand")
				{ rc.m_cmd = RAND; stk += 1; }
			else
			{
				for (std::size_t i = 0; i < inputs.size(); i++)
				{
					if (inputs[i] == cmd)
					{
						rc.m_cmd = PUSH_INPUT;
						rc.m_param = static_cast<NT>(i);
						stk += 1;
						break;
					}
				}
				if (rc.m_cmd != PUSH_INPUT)
				{
					rc.m_cmd = PUSH_CONST;
					bool err(false);
					rc.m_param = plib::pstonum_ne<decltype(rc.m_param)>(cmd, err);
					if (err)
						throw plib::pexception(plib::pfmt("pfunction: unknown/misformatted token <{1}> in <{2}>")(cmd)(expr));
					stk += 1;
				}
			}
			if (stk < 1)
				throw plib::pexception(plib::pfmt("pfunction: stack underflow on token <{1}> in <{2}>")(cmd)(expr));
			m_precompiled.push_back(rc);
		}
		if (stk != 1)
			throw plib::pexception(plib::pfmt("pfunction: stack count different to one on <{2}>")(expr));
	}

	static int get_prio(const pstring &v)
	{
		if (v == "(" || v == ")")
			return 1;
		else if (plib::left(v, 1) >= "a" && plib::left(v, 1) <= "z")
			return 0;
		else if (v == "*" || v == "/")
			return 20;
		else if (v == "+" || v == "-")
			return 10;
		else if (v == "^")
			return 30;
		else
			return -1;
	}

	static pstring pop_check(std::stack<pstring> &stk, const pstring &expr)
	{
		if (stk.size() == 0)
			throw plib::pexception(plib::pfmt("pfunction: stack underflow during infix parsing of: <{1}>")(expr));
		pstring res = stk.top();
		stk.pop();
		return res;
	}

	template <typename NT>
	void pfunction<NT>::compile_infix(const std::vector<pstring> &inputs, const pstring &expr)
	{
		// Shunting-yard infix parsing
		std::vector<pstring> sep = {"(", ")", ",", "*", "/", "+", "-", "^"};
		std::vector<pstring> sexpr(plib::psplit(plib::replace_all(expr, " ", ""), sep));
		std::stack<pstring> opstk;
		std::vector<pstring> postfix;

		for (std::size_t i = 0; i < sexpr.size(); i++)
		{
			pstring &s = sexpr[i];
			if (s=="(")
				opstk.push(s);
			else if (s==")")
			{
				pstring x = pop_check(opstk, expr);
				while (x != "(")
				{
					postfix.push_back(x);
					x = pop_check(opstk, expr);
				}
				if (opstk.size() > 0 && get_prio(opstk.top()) == 0)
					postfix.push_back(pop_check(opstk, expr));
			}
			else if (s==",")
			{
				pstring x = pop_check(opstk, expr);
				while (x != "(")
				{
					postfix.push_back(x);
					x = pop_check(opstk, expr);
				}
				opstk.push(x);
			}
			else {
				int p = get_prio(s);
				if (p>0)
				{
					if (opstk.size() == 0)
						opstk.push(s);
					else
					{
						if (get_prio(opstk.top()) >= get_prio(s))
							postfix.push_back(pop_check(opstk, expr));
						opstk.push(s);
					}
				}
				else if (p == 0) // Function or variable
				{
					if (sexpr[i+1] == "(")
						opstk.push(s);
					else
						postfix.push_back(s);
				}
				else
					postfix.push_back(s);
			}
		}
		while (opstk.size() > 0)
		{
			postfix.push_back(opstk.top());
			opstk.pop();
		}
		compile_postfix(inputs, postfix, expr);
	}


	#define ST1 stack[ptr]
	#define ST2 stack[ptr-1]

	#define OP(OP, ADJ, EXPR) \
	case OP: \
		ptr-= (ADJ); \
		stack[ptr-1] = (EXPR); \
		break;

	template <typename NT>
	NT pfunction<NT>::evaluate(const std::vector<NT> &values) noexcept
	{
		std::array<NT, 20> stack = { 0 };
		unsigned ptr = 0;
		stack[0] = 0.0;
		for (auto &rc : m_precompiled)
		{
			switch (rc.m_cmd)
			{
				OP(ADD,  1, ST2 + ST1)
				OP(MULT, 1, ST2 * ST1)
				OP(SUB,  1, ST2 - ST1)
				OP(DIV,  1, ST2 / ST1)
				OP(POW,  1, std::pow(ST2, ST1))
				OP(SIN,  0, std::sin(ST2))
				OP(COS,  0, std::cos(ST2))
				OP(TRUNC,  0, std::trunc(ST2))
				case RAND:
					stack[ptr++] = lfsr_random();
					break;
				case PUSH_INPUT:
					stack[ptr++] = values[static_cast<unsigned>(rc.m_param)];
					break;
				case PUSH_CONST:
					stack[ptr++] = rc.m_param;
					break;
			}
		}
		return stack[ptr-1];
	}

	template class pfunction<float>;
	template class pfunction<double>;

} // namespace plib
@ $000006 -> mainkey = key[3], globalkey = { key[1], key[2], $00 } driver FD1094 SP plain SP enc PC plain PC enc States Used -------- -------- -------- -------- -------- -------- ----------- aceattaa 317-0060 00000000 A711AF59 00000400 AF59EADD 00 17 31 45 90 FC altbeaj3 317-0068 FFFFFF00 B2F7F299 00000400 CCDDEF58 00 0F 18 20 93 A7 D8 altbeaj1 317-0065 C9C5F299 CCDDECDD bayroute 317-0116 00504000 5EB40000 00001000 5533A184 00 04 11 18 bayroutj 317-0115 00504000 56150000 00001000 85948DCF 00 05 12 16 bullet 317-0041 00000000 57355D96 00001882 8DDC8CF4 (deduced, not 100% sure) cotton 317-0181a 00204000 5DB20000 00000716 CCDD0716 00 0E 73 cottonu 317-0180 00204000 5DB20000 00000716 A1840716 00 0E 73 cottonj 317-0179a 00204000 5DB20000 00000716 CCDD0716 00 0E 73 ddux 317-0096 00000000 5F94AF59 00000406 AF5987A0 00 21 28 70 D9 eswat 317-0130 00000000 A711AF59 00000400 5533BC59 00 05 0C EC FA eswatu 317-0129 00000000 5537AF59 00000400 55334735 00 0A 12 C3 CC eswatj 317-0128 00000000 A711AF59 00000400 55334735 00 63 CB D5 exctleag 317-0079? 00000000 5537AF59 00000410 83018384 (deduced, not 100% sure) fpoint 317-0127A 00000000 AF59AF59 00001A40 8DDC9960 00 15 35 5F 82 DB fpoint1 317-0127A 00000000 AF59AF59 00001A40 8DDC9960 00 15 35 5F 82 DB goldnaxu 317-0122 FFFFFF00 E53AF2B9 00000400 A184A196 00 03 51 72 99 F6 goldnaxj 317-0121 FFFFFF00 C9D6F2B9 00000400 AF59A785 00 12 35 58 7A 9E goldnax3 317-0120 FFFFFF00 ED62F2B9 00000400 AF59A785 00 0A 0D 44 C7 EF goldnax1 317-0110 FFFFFF00 ED62F2B9 00000400 AF59A785 00 19 2E 31 48 5D mvp 317-0143 00000000 5F94A711 00000416 BD59DC5B 00 19 20 88 98 mvpj 317-0142 00000000 5F94AF59 00000416 BD599C7D 00 19 35 91 DA passsht 317-0080 00000000 AF59AF59 00003202 C2003923 00 11 52 96 EE passshta 317-0074 00000000 AF59AF59 000031E4 C2003F8C 00 12 47 83 A7 passshtj 317-0070 00000000 5D92AF59 000031E4 C2003F8C 00 12 59 83 FE ryukyu 317-5023 00203800 AF49D30B 0000042E FC5863B5 00 DC EF shinobi2 317-0049 FFFFFF00 C9C5F25F 00000400 AF598395 00 53 88 9B 9C F1 sonicbom 317-0053 00000000 5735AF59 00001000 FC587133 00 suprleag 317-0045? 00000000 A711AF59 BD59CE5B tetris2 317-0092 00000000 5735AF59 00000410 AF598685 00 10 52 74 97 FC tetris1 317-0091 00000000 5D92AF59 00000410 AF59AE58 99 25 42 5B 68 FC wb34 317-0087 FFFFFF7E B2978997 00000500 AF590500 00 11 64 69 82 wb33 317-0089 FFFFFF7E E5C78997 00000500 AF590500 00 23 40 52 71 wb32 317-0085 FFFFFF7E B2F78997 00000500 AF590500 00 10 13 26 77 wrestwa2 317-0102 00000000 5D96AF59 00000414 EE588E5B 00 12 A7 AB CC F9 FC wrestwa1 317-0090 00000000 5D96AF59 00000414 8301AE18 00 12 A7 AB CC F9 FC suprleag pc possibilities: 101E -> follows an RTS 108E -> follows 3 NOPs 11C4 11C8 1212 1214 1218 1282 1284 1288 1342 1416 141C 1486 148C 1606 1E52 1E54 bullet pc possibilities: 0822 0824 0882 0884 0C08 137C 1822 1824 1882 1884 1C08 tetris1: 410: 4ff9 0000 0000 lea $0.l, a7 416: 46fc 2700 move #$2700, sr 41a: 0c80 005b ffff cmpi.l #$5bffff, d0 400: 4e71 nop 402: 4e73 rte tetris2: 410: 4ff9 0000 0000 lea $0.l, a7 416: 46fc 2700 move #$2700, sr 41a: 0c80 0052 ffff cmpi.l #$52ffff, d0 400: 4e71 nop 402: 4e73 rte wrestwa1: 414: 4ff8 0000 lea $0.w, a7 418: 46fc 2700 move #$2700, sr 41c: 0c80 00fc ffff cmpi.l #$fcffff, d0 mvp: 416: 4ff8 0000 lea $0.w, a7 41a: 46fc 2700 move #$2700, sr 41e: 7000 moveq #0, d0 420: 2200 move.l d0, d1 ... 42c: 2e00 move.l d0, d7 42e: 2040 movea.l d0, a0 ... 43a: 2c40 movea.l d0, a6 43c: 0c80 0098 ffff cmpi.l #$98ffff, d0 wb34: 500: 46fc 2700 move #$2700, sr 504: 0c80 0064 ffff cmpi.l #$64ffff, d0 goldnaxu: 400: 6000 000c bra $40e 40e: 4ff8 ff00 lea $ff00.w, a7 412: 46fc 2700 move #$2700, sr 416: 0c80 0072 ffff cmpi.l #$72ffff, d0 ryukyu: 42e: 4e71 nop ... 440: 0c80 00dc ffff cmpi.l #$dcffff, d0 eswat: 400: 4ff8 0000 lea $0.w, a7 404: 46fc 2700 move #$2700, sr 408: 0c80 000c ffff cmpi.l #$cffff, d0 *****************************************************************************/ #include "emu.h" #include "fd1094.h" //************************************************************************** // CONSTANTS //************************************************************************** // device type definition const device_type FD1094 = &device_creator<fd1094_device>; /* 317-0162 CPU also needs to mask: 0x107a, 0x127a, 0x147a, 0x167a, 0x187a, 0x1a7a, 0x1c7a, 0x1e7a, this only happens with 317-0162 so far; I assume it is a fault in the CPU. */ const UINT16 fd1094_device::s_masked_opcodes[] = { 0x013a,0x033a,0x053a,0x073a,0x083a,0x093a,0x0b3a,0x0d3a,0x0f3a, 0x103a, 0x10ba,0x10fa, 0x113a,0x117a,0x11ba,0x11fa, 0x123a, 0x12ba,0x12fa, 0x133a,0x137a,0x13ba,0x13fa, 0x143a, 0x14ba,0x14fa, 0x153a,0x157a,0x15ba, 0x163a, 0x16ba,0x16fa, 0x173a,0x177a,0x17ba, 0x183a, 0x18ba,0x18fa, 0x193a,0x197a,0x19ba, 0x1a3a, 0x1aba,0x1afa, 0x1b3a,0x1b7a,0x1bba, 0x1c3a, 0x1cba,0x1cfa, 0x1d3a,0x1d7a,0x1dba, 0x1e3a, 0x1eba,0x1efa, 0x1f3a,0x1f7a,0x1fba, 0x203a,0x207a,0x20ba,0x20fa, 0x213a,0x217a,0x21ba,0x21fa, 0x223a,0x227a,0x22ba,0x22fa, 0x233a,0x237a,0x23ba,0x23fa, 0x243a,0x247a,0x24ba,0x24fa, 0x253a,0x257a,0x25ba, 0x263a,0x267a,0x26ba,0x26fa, 0x273a,0x277a,0x27ba, 0x283a,0x287a,0x28ba,0x28fa, 0x293a,0x297a,0x29ba, 0x2a3a,0x2a7a,0x2aba,0x2afa, 0x2b3a,0x2b7a,0x2bba, 0x2c3a,0x2c7a,0x2cba,0x2cfa, 0x2d3a,0x2d7a,0x2dba, 0x2e3a,0x2e7a,0x2eba,0x2efa, 0x2f3a,0x2f7a,0x2fba, 0x303a,0x307a,0x30ba,0x30fa, 0x313a,0x317a,0x31ba,0x31fa, 0x323a,0x327a,0x32ba,0x32fa, 0x333a,0x337a,0x33ba,0x33fa, 0x343a,0x347a,0x34ba,0x34fa, 0x353a,0x357a,0x35ba, 0x363a,0x367a,0x36ba,0x36fa, 0x373a,0x377a,0x37ba, 0x383a,0x387a,0x38ba,0x38fa, 0x393a,0x397a,0x39ba, 0x3a3a,0x3a7a,0x3aba,0x3afa, 0x3b3a,0x3b7a,0x3bba, 0x3c3a,0x3c7a,0x3cba,0x3cfa, 0x3d3a,0x3d7a,0x3dba, 0x3e3a,0x3e7a,0x3eba,0x3efa, 0x3f3a,0x3f7a,0x3fba, 0x41ba,0x43ba,0x44fa,0x45ba,0x46fa,0x47ba,0x49ba,0x4bba,0x4cba,0x4cfa,0x4dba,0x4fba, 0x803a,0x807a,0x80ba,0x80fa, 0x81fa, 0x823a,0x827a,0x82ba,0x82fa, 0x83fa, 0x843a,0x847a,0x84ba,0x84fa, 0x85fa, 0x863a,0x867a,0x86ba,0x86fa, 0x87fa, 0x883a,0x887a,0x88ba,0x88fa, 0x89fa, 0x8a3a,0x8a7a,0x8aba,0x8afa, 0x8bfa, 0x8c3a,0x8c7a,0x8cba,0x8cfa, 0x8dfa, 0x8e3a,0x8e7a,0x8eba,0x8efa, 0x8ffa, 0x903a,0x907a,0x90ba,0x90fa, 0x91fa, 0x923a,0x927a,0x92ba,0x92fa, 0x93fa, 0x943a,0x947a,0x94ba,0x94fa, 0x95fa, 0x963a,0x967a,0x96ba,0x96fa, 0x97fa, 0x983a,0x987a,0x98ba,0x98fa, 0x99fa, 0x9a3a,0x9a7a,0x9aba,0x9afa, 0x9bfa, 0x9c3a,0x9c7a,0x9cba,0x9cfa, 0x9dfa, 0x9e3a,0x9e7a,0x9eba,0x9efa, 0x9ffa, 0xb03a,0xb07a,0xb0ba,0xb0fa, 0xb1fa, 0xb23a,0xb27a,0xb2ba,0xb2fa, 0xb3fa, 0xb43a,0xb47a,0xb4ba,0xb4fa, 0xb5fa, 0xb63a,0xb67a,0xb6ba,0xb6fa, 0xb7fa, 0xb83a,0xb87a,0xb8ba,0xb8fa, 0xb9fa, 0xba3a,0xba7a,0xbaba,0xbafa, 0xbbfa, 0xbc3a,0xbc7a,0xbcba,0xbcfa, 0xbdfa, 0xbe3a,0xbe7a,0xbeba,0xbefa, 0xbffa, 0xc03a,0xc07a,0xc0ba,0xc0fa, 0xc1fa, 0xc23a,0xc27a,0xc2ba,0xc2fa, 0xc3fa, 0xc43a,0xc47a,0xc4ba,0xc4fa, 0xc5fa, 0xc63a,0xc67a,0xc6ba,0xc6fa, 0xc7fa, 0xc83a,0xc87a,0xc8ba,0xc8fa, 0xc9fa, 0xca3a,0xca7a,0xcaba,0xcafa, 0xcbfa, 0xcc3a,0xcc7a,0xccba,0xccfa, 0xcdfa, 0xce3a,0xce7a,0xceba,0xcefa, 0xcffa, 0xd03a,0xd07a,0xd0ba,0xd0fa, 0xd1fa, 0xd23a,0xd27a,0xd2ba,0xd2fa, 0xd3fa, 0xd43a,0xd47a,0xd4ba,0xd4fa, 0xd5fa, 0xd63a,0xd67a,0xd6ba,0xd6fa, 0xd7fa, 0xd83a,0xd87a,0xd8ba,0xd8fa, 0xd9fa, 0xda3a,0xda7a,0xdaba,0xdafa, 0xdbfa, 0xdc3a,0xdc7a,0xdcba,0xdcfa, 0xddfa, 0xde3a,0xde7a,0xdeba,0xdefa, 0xdffa }; //************************************************************************** // DECRYPTION CACHE HELPER //************************************************************************** //------------------------------------------------- // fd1094_decryption_cache - constructor //------------------------------------------------- fd1094_decryption_cache::fd1094_decryption_cache(fd1094_device &fd1094) : m_fd1094(fd1094), m_baseaddress(0), m_size(0), m_rgnoffset(0) { reset(); } //------------------------------------------------- // reset - reset the cache //------------------------------------------------- void fd1094_decryption_cache::reset() { // reset all allocated cache buffers for (int cache = 0; cache < 256; cache++) m_decrypted_opcodes[cache].reset(); } //------------------------------------------------- // configure - configure the address and size // of the region we are caching //------------------------------------------------- void fd1094_decryption_cache::configure(offs_t baseaddress, UINT32 size, offs_t rgnoffset) { // if something important changes, throw away what we have if (m_baseaddress != baseaddress || m_size != size || m_rgnoffset != rgnoffset) { m_baseaddress = baseaddress; m_size = size; m_rgnoffset = rgnoffset; reset(); } } //------------------------------------------------- // decrypted_opcodes - return a pointer to the // decrypted opcodes for the given state //------------------------------------------------- UINT16 *fd1094_decryption_cache::decrypted_opcodes(UINT8 state) { // if we have already decrypted this state, use it if (m_decrypted_opcodes[state].count() > 0) return m_decrypted_opcodes[state]; // otherwise, allocate and decrypt m_decrypted_opcodes[state].resize(m_size); m_fd1094.decrypt(m_baseaddress, m_size, m_rgnoffset, m_decrypted_opcodes[state], state); return m_decrypted_opcodes[state]; } //************************************************************************** // CORE IMPLEMENTATION //************************************************************************** //------------------------------------------------- // fd1094_device - constructor //------------------------------------------------- fd1094_device::fd1094_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : m68000_device(mconfig, tag, owner, clock, "fd1094", __FILE__), m_state(0x00), m_irqmode(false), m_cache(*this), m_srcbase(NULL), m_srcbytes(0), m_key(NULL) { // override the name after the m68000 initializes m_name.cpy("FD1094"); // create the initial masked opcode table memset(m_masked_opcodes_lookup, 0, sizeof(m_masked_opcodes_lookup)); for (int index = 0; index < ARRAY_LENGTH(s_masked_opcodes); index++) { UINT16 opcode = s_masked_opcodes[index]; m_masked_opcodes_lookup[0][opcode >> 4] |= 1 << ((opcode >> 1) & 7); m_masked_opcodes_lookup[1][opcode >> 4] |= 1 << ((opcode >> 1) & 7); } // add some more opcodes for the more aggressive table for (int opcode = 0; opcode < 65536; opcode += 2) if ((opcode & 0xff80) == 0x4e80 || (opcode & 0xf0f8) == 0x50c8 || (opcode & 0xf000) == 0x6000) m_masked_opcodes_lookup[1][opcode >> 4] |= 1 << ((opcode >> 1) & 7); } //------------------------------------------------- // change_state - set the current state of the // chip //------------------------------------------------- void fd1094_device::change_state(int newstate) { // adjust state and IRQ mode switch (newstate & 0x300) { case 0x0000: // 0x00xx: select state xx m_state = newstate & 0xff; break; case STATE_RESET: // 0x01xx: select state xx and exit irq mode m_state = newstate & 0xff; m_irqmode = false; break; case STATE_IRQ: // 0x02xx: enter irq mode m_irqmode = true; break; case STATE_RTE: // 0x03xx: exit irq mode m_irqmode = false; break; } // notify of the state change m_state_change(state()); // force a flush of the prefetch cache on any state change set_state_int(M68K_PREF_ADDR, 0x0010); } //************************************************************************** // DEVICE OVERRIDES //************************************************************************** //------------------------------------------------- // device_start - one-time device initialization //------------------------------------------------- void fd1094_device::device_start() { // start the base device m68000_device::device_start(); // find the key m_key = memregion("key")->base(); if (m_key == NULL) throw emu_fatalerror("FD1094 key region not found!"); // get a pointer to the ROM region if (region() != NULL) { m_srcbase = reinterpret_cast<UINT16 *>(region()->base()); m_srcbytes = region()->bytes(); } // if no ROM region, see if there's a memory share with our name else { memory_share *share = owner()->memshare(tag()); if (share != NULL) { m_srcbase = reinterpret_cast<UINT16 *>(share->ptr()); m_srcbytes = share->bytes(); } } // if we got nothing, error if (m_srcbase == NULL) throw emu_fatalerror("FD1094 found no data to decrypt!"); // if address 0 is mapped to ROM, assume this is a state memory mapping and // use the internal state change callback if (space(AS_PROGRAM).get_read_ptr(0) != NULL) m_state_change = state_change_delegate(FUNC(fd1094_device::default_state_change), this); // determine length and configure our cache m_cache.configure(0x000000, m_srcbytes, 0x000000); // register for the state changing callbacks we need in the m68000 set_cmpild_callback(write32_delegate(FUNC(fd1094_device::cmp_callback),this)); set_rte_callback(write_line_delegate(FUNC(fd1094_device::rte_callback),this)); static_set_irq_acknowledge_callback(*this, device_irq_acknowledge_delegate(FUNC(fd1094_device::irq_callback), this)); // save state save_item(NAME(m_state)); save_item(NAME(m_irqmode)); } //------------------------------------------------- // device_reset - one-time device initialization //------------------------------------------------- void fd1094_device::device_reset() { // flush the cache and switch to the reset state m_cache.reset(); change_state(STATE_RESET); // reset the parent m68000_device::device_reset(); } //------------------------------------------------- // device_postload - post restore initialization //------------------------------------------------- void fd1094_device::device_postload() { // refresh the state m_state_change(state()); } //************************************************************************** // INTERNAL HELPERS //************************************************************************** //------------------------------------------------- // decrypt_one - decrypt a single opcode given // the address, data, and keys; note that the // address provided is the word address // (physical address / 2) //------------------------------------------------- UINT16 fd1094_device::decrypt_one(offs_t address, UINT16 val, const UINT8 *main_key, UINT8 state, bool vector_fetch) { // extract and adjust the global key UINT8 gkey1 = main_key[1]; UINT8 gkey2 = main_key[2]; UINT8 gkey3 = main_key[3]; if (state & 0x0001) { gkey1 ^= 0x04; // global_xor1 gkey2 ^= 0x80; // key_1a invert gkey3 ^= 0x80; // key_2a invert } if (state & 0x0002) { gkey1 ^= 0x01; // global_swap2 gkey2 ^= 0x10; // key_7a invert gkey3 ^= 0x01; // key_4b invert } if (state & 0x0004) { gkey1 ^= 0x80; // key_0b invert gkey2 ^= 0x40; // key_6b invert gkey3 ^= 0x04; // global_swap4 } if (state & 0x0008) { gkey1 ^= 0x20; // global_xor0 gkey2 ^= 0x02; // key_6a invert gkey3 ^= 0x20; // key_5a invert } if (state & 0x0010) { gkey1 ^= 0x02; // key_0c invert gkey1 ^= 0x40; // key_5b invert gkey2 ^= 0x08; // key_4a invert } if (state & 0x0020) { gkey1 ^= 0x08; // key_1b invert gkey3 ^= 0x08; // key_3b invert gkey3 ^= 0x10; // global_swap1 } if (state & 0x0040) { gkey1 ^= 0x10; // key_2b invert gkey2 ^= 0x20; // global_swap0a gkey2 ^= 0x04; // global_swap0b } if (state & 0x0080) { gkey2 ^= 0x01; // key_3a invert gkey3 ^= 0x02; // key_0a invert gkey3 ^= 0x40; // global_swap3 } // for address xx0000-xx0006 (but only if >= 000008), use key xx2000-xx2006 UINT8 mainkey; if ((address & 0x0ffc) == 0 && address >= 4) mainkey = main_key[(address & 0x1fff) | 0x1000]; else mainkey = main_key[address & 0x1fff]; UINT8 key_F; if (address & 0x1000) key_F = BIT(mainkey,7); else key_F = BIT(mainkey,6); // the CPU has been verified to produce different results when fetching opcodes // from 0000-0006 than when fetching the inital SP and PC on reset. if (vector_fetch) { if (address <= 3) gkey3 = 0x00; // supposed to always be the case if (address <= 2) gkey2 = 0x00; if (address <= 1) gkey1 = 0x00; if (address <= 1) key_F = 0; } UINT8 global_xor0 = 1^BIT(gkey1,5); UINT8 global_xor1 = 1^BIT(gkey1,2); UINT8 global_swap2 = 1^BIT(gkey1,0); UINT8 global_swap0a = 1^BIT(gkey2,5); UINT8 global_swap0b = 1^BIT(gkey2,2); UINT8 global_swap3 = 1^BIT(gkey3,6); UINT8 global_swap1 = 1^BIT(gkey3,4); UINT8 global_swap4 = 1^BIT(gkey3,2); UINT8 key_0a = BIT(mainkey,0) ^ BIT(gkey3,1); UINT8 key_0b = BIT(mainkey,0) ^ BIT(gkey1,7); UINT8 key_0c = BIT(mainkey,0) ^ BIT(gkey1,1); UINT8 key_1a = BIT(mainkey,1) ^ BIT(gkey2,7); UINT8 key_1b = BIT(mainkey,1) ^ BIT(gkey1,3); UINT8 key_2a = BIT(mainkey,2) ^ BIT(gkey3,7); UINT8 key_2b = BIT(mainkey,2) ^ BIT(gkey1,4); UINT8 key_3a = BIT(mainkey,3) ^ BIT(gkey2,0); UINT8 key_3b = BIT(mainkey,3) ^ BIT(gkey3,3); UINT8 key_4a = BIT(mainkey,4) ^ BIT(gkey2,3); UINT8 key_4b = BIT(mainkey,4) ^ BIT(gkey3,0); UINT8 key_5a = BIT(mainkey,5) ^ BIT(gkey3,5); UINT8 key_5b = BIT(mainkey,5) ^ BIT(gkey1,6); UINT8 key_6a = BIT(mainkey,6) ^ BIT(gkey2,1); UINT8 key_6b = BIT(mainkey,6) ^ BIT(gkey2,6); UINT8 key_7a = BIT(mainkey,7) ^ BIT(gkey2,4); if (val & 0x8000) // block invariant: val & 0x8000 != 0 { val = BITSWAP16(val, 15, 9,10,13, 3,12, 0,14, 6, 5, 2,11, 8, 1, 4, 7); if (!global_xor1) if (~val & 0x0800) val ^= 0x3002; // 1,12,13 if (~val & 0x0020) val ^= 0x0044; // 2,6 if (!key_1b) if (~val & 0x0400) val ^= 0x0890; // 4,7,11 if (!global_swap2) if (!key_0c) val ^= 0x0308; // 3,8,9 val ^= 0x6561; if (!key_2b) val = BITSWAP16(val,15,10,13,12,11,14,9,8,7,6,0,4,3,2,1,5); // 0-5, 10-14 } if (val & 0x4000) // block invariant: val & 0x4000 != 0 { val = BITSWAP16(val, 13,14, 7, 0, 8, 6, 4, 2, 1,15, 3,11,12,10, 5, 9); if (!global_xor0) if (val & 0x0010) val ^= 0x0468; // 3,5,6,10 if (!key_3a) if (val & 0x0100) val ^= 0x0081; // 0,7 if (!key_6a) if (val & 0x0004) val ^= 0x0100; // 8 if (!key_5b) if (!key_0b) val ^= 0x3012; // 1,4,12,13 val ^= 0x3523; if (!global_swap0b) val = BITSWAP16(val, 2,14,13,12, 9,10,11, 8, 7, 6, 5, 4, 3,15, 1, 0); // 2-15, 9-11 } if (val & 0x2000) // block invariant: val & 0x2000 != 0 { val = BITSWAP16(val, 10, 2,13, 7, 8, 0, 3,14, 6,15, 1,11, 9, 4, 5,12); if (!key_4a) if (val & 0x0800) val ^= 0x010c; // 2,3,8 if (!key_1a) if (val & 0x0080) val ^= 0x1000; // 12 if (!key_7a) if (val & 0x0400) val ^= 0x0a21; // 0,5,9,11 if (!key_4b) if (!key_0a) val ^= 0x0080; // 7 if (!global_swap0a) if (!key_6b) val ^= 0xc000; // 14,15 val ^= 0x99a5; if (!key_5b) val = BITSWAP16(val,15,14,13,12,11, 1, 9, 8, 7,10, 5, 6, 3, 2, 4, 0); // 1,4,6,10 } if (val & 0xe000) // block invariant: val & 0xe000 != 0 { val = BITSWAP16(val,15,13,14, 5, 6, 0, 9,10, 4,11, 1, 2,12, 3, 7, 8); val ^= 0x17ff; if (!global_swap4) val = BITSWAP16(val, 15,14,13, 6,11,10, 9, 5, 7,12, 8, 4, 3, 2, 1, 0); // 5-8, 6-12 if (!global_swap3) val = BITSWAP16(val, 13,15,14,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0); // 15-14-13 if (!global_swap2) val = BITSWAP16(val, 15,14,13,12,11, 2, 9, 8,10, 6, 5, 4, 3, 0, 1, 7); // 10-2-0-7 if (!key_3b) val = BITSWAP16(val, 15,14,13,12,11,10, 4, 8, 7, 6, 5, 9, 1, 2, 3, 0); // 9-4, 3-1 if (!key_2a) val = BITSWAP16(val, 13,14,15,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0); // 13-15 if (!global_swap1) val = BITSWAP16(val, 15,14,13,12, 9, 8,11,10, 7, 6, 5, 4, 3, 2, 1, 0); // 11...8 if (!key_5a) val = BITSWAP16(val, 15,14,13,12,11,10, 9, 8, 4, 5, 7, 6, 3, 2, 1, 0); // 7...4 if (!global_swap0a) val = BITSWAP16(val, 15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 0, 3, 2, 1); // 3...0 } val = BITSWAP16(val, 12,15,14,13,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0); if ((val & 0xb080) == 0x8000) val ^= 0x4000; if ((val & 0xf000) == 0xc000) val ^= 0x0080; if ((val & 0xb100) == 0x0000) val ^= 0x4000; // mask out opcodes doing PC-relative addressing, replace them with FFFF if ((m_masked_opcodes_lookup[key_F][val >> 4] >> ((val >> 1) & 7)) & 1) val = 0xffff; return val; } //------------------------------------------------- // decrypt - decrypt a buffers' worth of opcodes //------------------------------------------------- void fd1094_device::decrypt(offs_t baseaddr, UINT32 size, const UINT16 *srcptr, UINT16 *opcodesptr, UINT8 state) { for (offs_t offset = 0; offset < size; offset += 2) opcodesptr[offset / 2] = decrypt_one((baseaddr + offset) / 2, srcptr[offset / 2], m_key, state, (baseaddr + offset) < 8); } //------------------------------------------------- // default_state_change - handle state changes // for standard cases //------------------------------------------------- void fd1094_device::default_state_change(UINT8 state) { space(AS_PROGRAM).set_decrypted_region(0x000000, m_srcbytes - 1, m_cache.decrypted_opcodes(state)); } //************************************************************************** // STATIC CALLBACKS //************************************************************************** //------------------------------------------------- // cmp_callback - callback for CMP.L instructions // (state change) //------------------------------------------------- WRITE32_MEMBER(fd1094_device::cmp_callback) { if (offset == 0 && (data & 0x0000ffff) == 0x0000ffff) change_state(data >> 16); } //------------------------------------------------- // irq_callback - callback when the FD1094 enters // interrupt code //------------------------------------------------- IRQ_CALLBACK_MEMBER( fd1094_device::irq_callback ) { change_state(STATE_IRQ); return (0x60 + irqline * 4) / 4; // vector address } //------------------------------------------------- // rte_callback - callback when an RTE instruction // is encountered //------------------------------------------------- WRITE_LINE_MEMBER(fd1094_device::rte_callback) { change_state(STATE_RTE); } #ifdef MAME_DEBUG /* // Possible: global=12A8F8E5 seed=0AD691 pc=1882 // Possible: global=12AAF8E5 seed=0AD691 pc=1882 // Possible: global=82A8F8EC seed=24921C pc=1882 // Possible: global=82AAF8EC seed=24921C pc=1882 // Possible: global=92A8F8EC seed=3D5C17 pc=1882 // Possible: global=92AAF8EC seed=3D5C17 pc=1882 static const fd1094_constraint bullet_constraints[] = { // main entry point { 0x001882, FD1094_STATE_RESET | 0x00, 0x4ff8, 0xffff }, // lea $0.w,a7 { 0x001884, FD1094_STATE_RESET | 0x00, 0x0000, 0xffff }, { 0x001886, FD1094_STATE_RESET | 0x00, 0x46fc, 0xffff }, // move #$2700,sr { 0x001888, FD1094_STATE_RESET | 0x00, 0x2700, 0xffff }, { 0x00188a, FD1094_STATE_RESET | 0x00, 0x0c80, 0xffff }, // cmpi.l #$00xxffff,d0 { 0x00188c, FD1094_STATE_RESET | 0x00, 0x0000, 0xff00 }, { 0x00188e, FD1094_STATE_RESET | 0x00, 0xffff, 0xffff }, // IRQ4 entry point { 0x000418, FD1094_STATE_IRQ | 0x00, 0x48e7, 0xffff }, // movem.l d0-d7/a0-a6,-(a7) { 0x00041a, FD1094_STATE_IRQ | 0x00, 0xfffe, 0xffff }, // IRQ4 exit points { 0x000612, FD1094_STATE_IRQ | 0x00, 0x4cdf, 0xffff }, // movem.l (a7)+,d0-d7/a0-a6 { 0x000614, FD1094_STATE_IRQ | 0x00, 0x7fff, 0xffff }, { 0x000616, FD1094_STATE_IRQ | 0x00, 0x4e73, 0xffff }, // rte { 0 } }; // Possible: global=FCAFF9F9 seed=177AC6 pc=0400 static const fd1094_constraint altbeaj1_constraints[] = { // main entry point { 0x000400, FD1094_STATE_RESET | 0x00, 0x6000, 0xffff }, // bra $40e { 0x000402, FD1094_STATE_RESET | 0x00, 0x000c, 0xffff }, { 0x00040e, FD1094_STATE_RESET | 0x00, 0x4ff8, 0xffff }, // lea $ff00.w,a7 { 0x000410, FD1094_STATE_RESET | 0x00, 0xff00, 0xffff }, { 0x000412, FD1094_STATE_RESET | 0x00, 0x46fc, 0xffff }, // move #$2700,sr { 0x000414, FD1094_STATE_RESET | 0x00, 0x2700, 0xffff }, { 0x000416, FD1094_STATE_RESET | 0x00, 0x0c80, 0xffff }, // cmpi.l #$00xxffff,d0 { 0x000418, FD1094_STATE_RESET | 0x00, 0x0000, 0xff00 }, { 0x00041a, FD1094_STATE_RESET | 0x00, 0xffff, 0xffff }, // IRQ4 entry point { 0x000404, FD1094_STATE_IRQ | 0x00, 0x6000, 0xffff }, // bra $2ac4 { 0x000406, FD1094_STATE_IRQ | 0x00, 0x26be, 0xffff }, { 0x002ac4, FD1094_STATE_IRQ | 0x00, 0x48e7, 0xffff }, // movem.l d0-d7/a0-a6,-(a7) { 0x002ac6, FD1094_STATE_IRQ | 0x00, 0xfffe, 0xffff }, // IRQ4 exit points { 0x002ca4, FD1094_STATE_IRQ | 0x00, 0x4cdf, 0xffff }, // movem.l (a7)+,d0-d7/a0-a6 { 0x002ca6, FD1094_STATE_IRQ | 0x00, 0x7fff, 0xffff }, { 0x002ca8, FD1094_STATE_IRQ | 0x00, 0x4e73, 0xffff }, // rte { 0x002cc4, FD1094_STATE_IRQ | 0x00, 0x3f3c, 0xffff }, // move #$2300,-(a7) { 0x002cc6, FD1094_STATE_IRQ | 0x00, 0x2300, 0xffff }, { 0x002cc8, FD1094_STATE_IRQ | 0x00, 0x4e73, 0xffff }, // rte // other IRQ entry points { 0x000408, FD1094_STATE_IRQ | 0x00, 0x6000, 0xffff }, // bra $40c { 0x00040a, FD1094_STATE_IRQ | 0x00, 0x0002, 0xffff }, { 0x00040c, FD1094_STATE_IRQ | 0x00, 0x4e73, 0xffff }, // rte { 0 } }; */ #endif