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