1 // license:GPL-2.0+
2 // copyright-holders:David Caldwell
3 /***************************************************************************
4 
5   jrcrypt.cpp
6 
7   This file is not part of MAME. It is here to provide detailed
8   documentation of the encryption used by Jr. Pac Man ROMs.
9 
10     David Caldwell 6-1-97
11     bug reports and comments to:
12     david@indigita.com
13 
14     This code is published under the GNU Public License. (GPL)
15 
16 ***************************************************************************/
17 
18 #include "emu.h"
19 #include <cctype>
20 
21 static int irq_mask;
22 
23 typedef uint16_t word;
24 typedef uint8_t byte;
25 
26 #define PreDecryptedRoms
27 
28 #ifndef PreDecryptedRoms
29 static int s0,s1,s2,s3; /* 1 bit registers inside decoder PAL */
30 static uint8_t shadowROM[0xffff];
31 static uint8_t used[0xFFFF];
32 uint32_t numberUsed = 0;
33 #else
34 struct {
35 	int count;
36 	int value;
37 } Jr_PacManTable[] = {
38 	{ 0x00C1, 0x00 },
39 	{ 0x0002, 0x80 },
40 	{ 0x0004, 0x00 },
41 	{ 0x0006, 0x80 },
42 	{ 0x0003, 0x00 },
43 	{ 0x0002, 0x80 },
44 	{ 0x0009, 0x00 },
45 	{ 0x0004, 0x80 },
46 	{ 0x9968, 0x00 },
47 	{ 0x0001, 0x80 },
48 	{ 0x0002, 0x00 },
49 	{ 0x0001, 0x80 },
50 	{ 0x0009, 0x00 },
51 	{ 0x0002, 0x80 },
52 	{ 0x0009, 0x00 },
53 	{ 0x0001, 0x80 },
54 	{ 0x00AF, 0x00 },
55 	{ 0x000E, 0x04 },
56 	{ 0x0002, 0x00 },
57 	{ 0x0004, 0x04 },
58 	{ 0x001E, 0x00 },
59 	{ 0x0001, 0x80 },
60 	{ 0x0002, 0x00 },
61 	{ 0x0001, 0x80 },
62 	{ 0x0002, 0x00 },
63 	{ 0x0002, 0x80 },
64 	{ 0x0009, 0x00 },
65 	{ 0x0002, 0x80 },
66 	{ 0x0009, 0x00 },
67 	{ 0x0002, 0x80 },
68 	{ 0x0083, 0x00 },
69 	{ 0x0001, 0x04 },
70 	{ 0x0001, 0x01 },
71 	{ 0x0001, 0x00 },
72 	{ 0x0002, 0x05 },
73 	{ 0x0001, 0x00 },
74 	{ 0x0003, 0x04 },
75 	{ 0x0003, 0x01 },
76 	{ 0x0002, 0x00 },
77 	{ 0x0001, 0x04 },
78 	{ 0x0003, 0x01 },
79 	{ 0x0003, 0x00 },
80 	{ 0x0003, 0x04 },
81 	{ 0x0001, 0x01 },
82 	{ 0x002E, 0x00 },
83 	{ 0x0078, 0x01 },
84 	{ 0x0001, 0x04 },
85 	{ 0x0001, 0x05 },
86 	{ 0x0001, 0x00 },
87 	{ 0x0001, 0x01 },
88 	{ 0x0001, 0x04 },
89 	{ 0x0002, 0x00 },
90 	{ 0x0001, 0x01 },
91 	{ 0x0001, 0x04 },
92 	{ 0x0002, 0x00 },
93 	{ 0x0001, 0x01 },
94 	{ 0x0001, 0x04 },
95 	{ 0x0002, 0x00 },
96 	{ 0x0001, 0x01 },
97 	{ 0x0001, 0x04 },
98 	{ 0x0001, 0x05 },
99 	{ 0x0001, 0x00 },
100 	{ 0x0001, 0x01 },
101 	{ 0x0001, 0x04 },
102 	{ 0x0002, 0x00 },
103 	{ 0x0001, 0x01 },
104 	{ 0x0001, 0x04 },
105 	{ 0x0002, 0x00 },
106 	{ 0x0001, 0x01 },
107 	{ 0x0001, 0x04 },
108 	{ 0x0001, 0x05 },
109 	{ 0x0001, 0x00 },
110 	{ 0x01B0, 0x01 },
111 	{ 0x0001, 0x00 },
112 	{ 0x0002, 0x01 },
113 	{ 0x00AD, 0x00 },
114 	{ 0x0031, 0x01 },
115 	{ 0x005C, 0x00 },
116 	{ 0x0005, 0x01 },
117 	{ 0x604E, 0x00 },
118 	{ 0,0 }
119 };
120 #endif
121 
MACHINE_RESET(jrpacman)122 MACHINE_RESET( jrpacman )
123 {
124 #ifndef PreDecryptedRoms
125 	s0 = 1;
126 	s1 = 1;
127 	s2 = 0;
128 	s3 = 0;
129 
130 	memset(shadowROM,0,sizeof(shadowROM));
131 	memset(used,0,sizeof(used));
132 #endif
133 }
134 
135 
136 #ifdef PreDecryptedRoms
jrpacman_decode_roms(int address)137 unsigned jrpacman_decode_roms(int address)
138 {
139 	int i;
140 	int cumulative=0;
141 	for (i=0;Jr_PacManTable[i].count;i++)
142 	{
143 		cumulative += Jr_PacManTable[i].count;
144 		if (cumulative > address)
145 			return RAM[address] ^ Jr_PacManTable[i].value;
146 	}
147 	return RAM[address]; // this should never happen!
148 }
149 #else
WordBit(word theWord,int theBit)150 static inline WordBit(word theWord, int theBit)
151 {
152 	return (theWord >> theBit)&1;
153 }
ByteBit(byte theByte,int theBit)154 static inline ByteBit(byte theByte, int theBit)
155 {
156 	return (theByte >> theBit)&1;
157 }
158 
159 int jrpacman_romdecode(int offset);
jrpacman_romdecodeA(int offset)160 int jrpacman_romdecodeA(int offset)
161 {
162 	jrpacman_romdecode(offset);
163 }
164 
jrpacman_romdecodeB(int offset)165 int jrpacman_romdecodeB(int offset)
166 {
167 	jrpacman_romdecode(offset + 0x8000);
168 }
169 
170 
171 /****************************************************************************
172 
173     This function would look suprisingly similiar to the source code to
174 the encoder pals, I imagine. I wrote is based on the jedec file that I
175 got from VideoDoctor. It needs to be run during the game, since the
176 logic is kind of weird and state based. This is slow. So you'll see
177 that I made it store its "decrypted" opcodes in another array. When I
178 ran the program and was satisfied that it had decrypted enough code, I
179 dumped this new array out into some ROM files.  These ROM files are
180 effectively decrypted. But nobody else has them, so I'd have to upload
181 them to some ROM archive somewhere and hope for the best. Besides, If
182 I ever found a bug in them its not easy to upgrade every ROM archive
183 site out there. So I created a table that has the decrypted ROMs and
184 the encrypted ROMs xor-ed with each other.  So if something wrong is
185 ever discovered , all that is needed to update is the table.  Jr. Pac
186 only messes with 3 bits (d0, d2 and d7) so the table doesn't look too
187 excited.  Also I run length encoded it so its not unwieldly.
188 
189     A lot of the functions that are ifdef-ed out here were debugging
190 functions that I used to help me during the decoding process.
191 
192  -David Caldwell
193  david@indigita.com
194 
195 ***************************************************************************/
196 
jrpacman_romdecode(int offset)197 int jrpacman_romdecode(int offset)
198 {
199 	int addressBus = offset;
200 	Z80_Regs Regs;
201 	Z80_GetRegs(&Regs);
202 
203 	{
204 	int m1 = !Regs.M1;//active low (supposedly means opcode read)
205 
206 	/* Pal 8C (16L8) */
207 	int pcbe =  !(addressBus >= 0x0000 && addressBus <= 0x3fff ||
208 					addressBus >= 0x8000 && addressBus <=
209 0xdfff);
210 
211 	int sop0 =  !(addressBus >= 0x0000 && addressBus <= 0x001f ||
212 					addressBus >= 0x00e0 && addressBus <=
213 0x00ff ||
214 					addressBus >= 0x9a60 && addressBus <=
215 0x9a7f ||
216 					addressBus >= 0x9ac0 && addressBus <=
217 0x9adf ||
218 					addressBus >= 0x9b60 && addressBus <=
219 0x9b7f ||
220 					addressBus >= 0x9be0 && addressBus <=
221 0x9bff && m1);
222 
223 	int sop1 =  !(addressBus >= 0x9be0 && addressBus <= 0x9bff && m1 ||
224 					addressBus >= 0x9ca0 && addressBus <=
225 0x9cbf);
226 
227 	int sop2 =  !(addressBus >= 0x00c0 && addressBus <= 0x00df ||
228 					addressBus >= 0x9a40 && addressBus <=
229 0x9a5f);
230 
231 
232 	/* Pal 9c (16r4) */
233 	int md0  = ByteBit(RAM[addressBus],0);
234 	int md2  = ByteBit(RAM[addressBus],2);
235 	int md7  = ByteBit(RAM[addressBus],7);
236 
237 	int d0 =  !( s0 &&  s1 && !md0 ||
238 				!s0 &&  s1 &&  md0 ||
239 					s0 && !s1 && !md0 ||
240 				!s0 && !s1 && !md2);
241 
242 	int d2 =  !( s0 &&  s1 && !md2 ||
243 				!s0 &&  s1 && !md2 ||
244 					s0 && !s1 &&  md2 ||
245 				!s0 && !s1 && !md0);
246 
247 	int d7 =  !( s2 &&  s3  ||
248 				!s2 && !md7);
249 
250 	int pb1 = !( sop0 &&  s0 ||
251 				!sop0 && !s0);
252 
253 	int ns0 =  ( sop1 &&  s0   ||
254 				!sop1 && !s0   ||
255 				!sop0 &&  sop1);
256 
257 	int ns1 =  ( sop1 &&  s1 && !pb1 ||
258 				!sop1 &&  s1 && !pb1 ||
259 					sop1 &&  s1 &&  pb1 ||
260 				!sop1 && !s1 &&  pb1 ||
261 				!sop0 &&  sop1);
262 
263 	int ns2 =  ( sop0 &&  sop1 &&  s2 ||
264 					sop0 && !sop1 &&  s2 ||
265 				!sop0 && !sop1 &&  s2 ||
266 					sop0 && !sop2);
267 
268 	int ns3 =  ( !md7 );
269 
270 //  DebugPrint("%04x: %02x & %02x | %02x = %02x",addressBus,RAM[addressBus],~(1<<0) & ~(1<<2) & ~(1<<7), (d0) | (d2<<2) | (d7<<7),(RAM[addressBus] & ~(1<<0) & ~(1<<2) & ~(1<<7)) | (d0) | (d2<<2) | (d7<<7));
271 /*  printf("%04x: %02x & %02x | %02x = %02x\n",addressBus,RAM[addressBus],~(1<<0) & ~(1<<2) & ~(1<<7), (d0) | (d2<<2) | (d7<<7),(RAM[addressBus] & ~(1<<0) & ~(1<<2) & ~(1<<7)) | (d0) | (d2<<2) | (d7<<7));
272     {static int i=0;
273     if (i++>100)
274     {
275         while (getchar()!='\n')
276             {}
277     }}*/
278 	{
279 		int temp= ((int)RAM[addressBus] & 0x7A) | ((d7<<7) | (d2<<2) | (d0));
280 
281 //      if (Z80_Trace==1)
282 			if (!used[addressBus])
283 			{
284 				used[addressBus]=1;
285 				shadowROM[addressBus] = temp;
286 				numberUsed++;
287 			}
288 			else
289 			{
290 				if (shadowROM[addressBus] != temp)
291 					DebugPrint("Address: %04x translates to 2 different values!!!! (%02x and %02x)",addressBus,shadowROM[addressBus],temp);
292 			}
293 
294 
295 		if (Z80_Trace==1)
296 		{
297 			static last = 0;
298 			if (last + 30 <= TickCount()) /* print bnanner if we havent been called in half a second */
299 				printf("m1   sop0 sop1 sop2 pcbe  md7  md2 md0   d7   d2   d0    pb1   s0   s1   s2   s3    ns0  ns1  ns2  ns3\n");
300 			last = TickCount();
301 			printf("%-4d %-4d %-4d %-4d %-4d  %-4d %-4d %-4d %-4d %-4d %-4d  %-4d  %-4d %-4d %-4d %-4d  %-4d %-4d %-4d %-4d     ",
302 					m1,  sop0,sop1,sop2,pcbe, md7, md2, md0,
303 d7,  d2,  d0,   pb1,  s0,  s1,  s2,  s3,   ns0, ns1, ns2, ns3);
304 			printf("%04x: %02x & %02x | %02x = %02x\n",addressBus,RAM[addressBus],~(1<<0) & ~(1<<2) & ~(1<<7), (d0) | (d2<<2) | (d7<<7),(RAM[addressBus] & ~(1<<0) & ~(1<<2) & ~(1<<7)) | (d0) | (d2<<2) | (d7<<7));
305 			Z80_Trace = 1; /* stop it if it was running for a count */
306 		}
307 
308 		/* latch new flip flops on rising edge of pcbe */
309 		if (!pcbe)
310 		{
311 			s0 = ns0;
312 			s1 = ns1;
313 			s2 = ns2;
314 			s3 = ns3;
315 		}
316 		return temp;
317 	}
318 	}
319 }
320 
321 int Z80_Trace=0;
322 
jr_monitor()323 void jr_monitor()
324 {
325 	int i;
326 	int encrypted=0;
327 	//int last_used=-1;
328 	int unmapped_encrypted=0;
329 	int unmapped=0;
330 	printf("statistics: \n");
331 	printf("  Successfully mapped: %d\n",numberUsed);
332 	for (i=0;i<0xe000;i=(i==0x4000?0x8000:i+1)) /* skip hole where
333 hardware and RAM is */
334 	{
335 		if (used[i])
336 		{
337 			if (shadowROM[i] != RAM[i])
338 				encrypted = 1;
339 			else
340 				encrypted = 0;
341 			//last_used = i;
342 		}
343 		else
344 		{
345 			if (encrypted)
346 				unmapped_encrypted++;
347 			else
348 				unmapped++;
349 		}
350 	}
351 	printf("  Non mapped, Probably not encrypted: %d\n",unmapped);
352 	printf("  Non mapped, but Probably encrypted: %d\n",unmapped_encrypted);
353 
354 	while (1)
355 	{
356 		void write_rom_section(char *prefix,char *suffix,int
357 start,int end);
358 		char c;
359 		printf(" Enter D to Merge mapped and unmapped and dump to rom file,\n");
360 		printf(" Enter Q to quit.\n");
361 		c=tolower(getchar());
362 		while (getchar()!='\n') {}
363 		if (c=='q')
364 			return;
365 		if (c=='d')
366 		{
367 			char line[100],*l;
368 			//int i;
369 			printf("Enter file prefix (files will be named 'prefix'.8d 'prefix'.8e, etc.\n");
370 			gets(line);
371 			// kill newline:
372 			for (l=line;*l!='\n' && *l!='\0';l++)
373 				{}
374 			*l = '\0';
375 
376 			write_rom_section(line,".8d",0x0000,0x2000);
377 			write_rom_section(line,".8e",0x2000,0x4000);
378 			write_rom_section(line,".8h",0x8000,0xa000);
379 			write_rom_section(line,".8j",0xa000,0xc000);
380 			write_rom_section(line,".8k",0xc000,0xe000);
381 		}
382 	}
383 }
384 
write_rom_section(char * prefix,char * suffix,int start,int end)385 void write_rom_section(char *prefix,char *suffix,int start,int end)
386 {
387 	FILE *out;
388 	char file[100];
389 	int i;
390 
391 	strcpy(file,prefix);
392 	strcat(file,suffix);
393 	out = fopen(file,"wb");
394 	for (i=start;i<end;i++)
395 		if (used[i])
396 			putc(shadowROM[i],out);
397 		else
398 			putc(RAM[i],out);
399 	fclose(out);
400 }
401 #endif
402 
jrpacman_interrupt_mask_w(uint8_t data)403 void jrpacman_interrupt_mask_w(uint8_t data)
404 {
405 	irq_mask = data;
406 }
407 
408 
409 
410 /***************************************************************************
411 
412   Interrupt handler. This function is called at regular intervals
413   (determined by IPeriod) by the CPU emulation.
414 
415 ***************************************************************************/
416 
417 static int IntVector = 0xff;    /* Here we store the interrupt vector, if
418 the code performs */
419 						/* an OUT to port $0. Not
420 all games do it: many use */
421 						/* Interrupt Mode 1, which
422 doesn't use an interrupt vector */
423 						/* (see Z80.c for details). */
424 
INTERRUPT_GEN(jrpacman_interrupt)425 INTERRUPT_GEN( jrpacman_interrupt )
426 {
427 	irq0_line_hold(device);
428 }
429 
430 
431 
432 /***************************************************************************
433 
434   The Pac Man machine uses OUT to port $0 to set the interrupt vector, so
435   we have to remember the value passed.
436 
437 ***************************************************************************/
jrpacman_out(byte Port,byte Value)438 void jrpacman_out(byte Port,byte Value)
439 {
440 	/* OUT to port $0 is used to set the interrupt vector */
441 	if (Port == 0) IntVector = Value;
442 }
443 
444 #if 0
445 /****************************************************************************
446 What follows is the program I used to create the JrPacMan_Table[] array at
447 the top of this file. It is included here for completeness.
448 ***************************************************************************/
449 
450 
451 // CreateJrDecodeTable.c
452 //
453 // Copyright David Caldwell
454 // This program is published under the GNU Public License.
455 //
456 // Comments, questions to: david@indigita.com
457 
458 #include <cstdio>
459 
460 typedef uint8_t byte;
461 
462 void CreateJrDecodeTable(byte *x, int length);
463 void Load(char *name,byte *buffer,int from, int length);
464 
465 byte encrypted[0x10000],decrypted[0x10000];
466 byte xored[0x10000];
467 void main()
468 {
469 	int i;
470 
471 	Load("jr8d",encrypted,0x0000,0x2000);
472 	Load("jr8e",encrypted,0x2000,0x2000);
473 	Load("jr8h",encrypted,0x8000,0x2000);
474 	Load("jr8j",encrypted,0xA000,0x2000);
475 	Load("jr8k",encrypted,0xC000,0x2000);
476 
477 	Load("1.8d",decrypted,0x0000,0x2000);
478 	Load("1.8e",decrypted,0x2000,0x2000);
479 	Load("1.8h",decrypted,0x8000,0x2000);
480 	Load("1.8j",decrypted,0xA000,0x2000);
481 	Load("1.8k",decrypted,0xC000,0x2000);
482 
483 	for (i=0;i<0x10000;i++)
484 		xored[i] = encrypted[i] ^ decrypted[i];
485 
486 	CreateJrDecodeTable(xored,0x10000);
487 }
488 
489 void Load(char *name,byte *buffer,int from, int length)
490 {
491 /*
492     emu_file file(options, nullptr, OPEN_FLAG_READ);
493     osd_file::error filerr = file.open(name);
494     if (filerr != osd_file::error::NONE)
495         return;
496     while (length--)
497         buffer[from++]=file->getc();
498 */
499 }
500 
501 void CreateJrDecodeTable(byte *x, int length)
502 {
503 	int i=0;
504 	byte last = 0;
505 	int count = 0;
506 
507 	printf( "struct {\n"
508 			"    int count;\n"
509 			"    int value;\n"
510 			"} Jr_PacManTable[] = {\n");
511 
512 	goto first;
513 
514 	for (i=0;i<length;i++)
515 	{
516 		if (x[i] != last)
517 		{
518 			printf("    { 0x%04X, 0x%02X },\n",count,last);
519 			count = 0;
520 		}
521 first:
522 		last = x[i];
523 		count++;
524 	}
525 
526 	printf("    { 0x%04X, 0x%02X },\n",count,last);
527 	printf( "    { 0,0 }\n"
528 			"};\n");
529 }
530 #endif
531