1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
10 * Pacbox - a Pacman Emulator for Rockbox
12 * Based on PIE - Pacman Instructional Emulator
14 * Copyright (c) 1997-2003,2004 Alessandro Scotti
15 * http://www.ascotti.org/
17 * This program is free software; you can redistribute it and/or
18 * modify it under the terms of the GNU General Public License
19 * as published by the Free Software Foundation; either version 2
20 * of the License, or (at your option) any later version.
22 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
23 * KIND, either express or implied.
25 ****************************************************************************/
32 extern const struct plugin_api
* rb
;
34 #ifndef HAVE_LCD_COLOR
35 /* Convert RGB888 to 2-bit greyscale - logic taken from bmp2rb.c */
36 static fb_data
rgb_to_gray(unsigned int r
, unsigned int g
, unsigned int b
)
38 int brightness
= ( 2*r
+ 4*g
+ b
);
39 if( r
== 0 && g
== 0 && b
== 0 )
42 brightness
= (brightness
/450);
43 if( brightness
> 2 ) return 0;
44 else return 2-brightness
;
48 unsigned char color_data_
[256] = {
49 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0b, 0x01,
50 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0b, 0x03,
51 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0b, 0x05,
52 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0b, 0x07,
53 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x01, 0x09,
54 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
55 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
56 0x00, 0x0f, 0x00, 0x0e, 0x00, 0x01, 0x0c, 0x0f,
57 0x00, 0x0e, 0x00, 0x0b, 0x00, 0x0c, 0x0b, 0x0e,
58 0x00, 0x0c, 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00,
59 0x00, 0x01, 0x02, 0x0f, 0x00, 0x07, 0x0c, 0x02,
60 0x00, 0x09, 0x06, 0x0f, 0x00, 0x0d, 0x0c, 0x0f,
61 0x00, 0x05, 0x03, 0x09, 0x00, 0x0f, 0x0b, 0x00,
62 0x00, 0x0e, 0x00, 0x0b, 0x00, 0x0e, 0x00, 0x0b,
63 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0e, 0x01,
64 0x00, 0x0f, 0x0b, 0x0e, 0x00, 0x0e, 0x00, 0x0f,
65 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
66 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
67 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
68 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
69 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
70 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
71 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
72 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
73 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
74 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
75 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
76 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
77 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
78 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
79 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
80 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
83 unsigned char palette_data_
[0x20] = {
84 0x00, 0x07, 0x66, 0xef, 0x00, 0xf8, 0xea, 0x6f,
85 0x00, 0x3f, 0x00, 0xc9, 0x38, 0xaa, 0xaf, 0xf6,
86 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
87 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
97 /* Putting this in IRAM actually slows down the iPods, but is good for
101 fb_data palette
[256] IBSS_ATTR
; /* Color palette */
103 fb_data palette
[256]; /* Color palette */
107 void init_PacmanMachine(int dip
)
111 /* Initialize the CPU and the RAM */
113 rb
->memset( &ram_
[0x4000], 0xFF, 0x1000 );
115 /* Initialize parameters */
120 /* Reset the machine */
121 reset_PacmanMachine();
123 /* Set the DIP switches to a default configuration */
124 setDipSwitches( dip
);
126 /* Initialize the video character translation tables: video memory has a
127 very peculiar arrangement in Pacman so we precompute a few tables to
128 move around faster */
130 for( i
=0x000; i
<0x400; i
++ ) {
137 else if( i
>= 0x3C0 ) {
139 y
= ((i
-0x3C0) >> 5);
142 x
= 27 - ((i
-0x40) >> 5);
143 y
= 2 + ((i
-0x40) & 0x1F);
145 if( (y
>= 0) && (y
< 36) && (x
>= 0) && (x
< 28) )
146 vchar_to_i_
[i
] = y
*28 + x
;
148 vchar_to_i_
[i
] = 0x3FF;
152 void reset_PacmanMachine(void)
158 interrupt_vector_
= 0;
160 rb
->memset( ram_
+0x4000, 0, 0x1000 );
161 rb
->memset( color_mem_
, 0, sizeof(color_mem_
) );
162 rb
->memset( video_mem_
, 0, sizeof(video_mem_
) );
163 rb
->memset( dirty_
, 0, sizeof(dirty_
) );
165 for( i
=0; i
<8; i
++ ) {
166 sprites_
[i
].color
= 0;
167 sprites_
[i
].x
= ScreenWidth
;
172 Run the machine for one frame.
176 /* Run until the CPU has executed the number of cycles per frame
177 (the function returns the number of "extra" cycles spent by the
178 last instruction but that is not really important here) */
180 unsigned extraCycles
= z80_run( CpuCyclesPerFrame
);
182 /* Reset the CPU cycle counter to make sure it doesn't overflow,
183 also take into account the extra cycles from the previous run */
185 setCycles( extraCycles
);
187 /* If interrupts are enabled, force a CPU interrupt with the vector
188 set by the program */
190 if( output_devices_
& InterruptEnabled
) {
191 z80_interrupt( interrupt_vector_
);
197 /** Returns the status of the coin lockout door. */
198 unsigned char getCoinLockout(void) {
199 return output_devices_
& CoinLockout
? 1 : 0;
202 static void decodeCharByte( unsigned char b
, unsigned char * charbuf
, int charx
, int chary
, int charwidth
)
206 for( i
=3; i
>=0; i
-- ) {
207 charbuf
[charx
+(chary
+i
)*charwidth
] = (b
& 1) | ((b
>> 3) & 2);
212 static void decodeCharLine( unsigned char * src
, unsigned char * charbuf
, int charx
, int chary
, int charwidth
)
216 for( x
=7; x
>=0; x
-- ) {
217 decodeCharByte( *src
++, charbuf
, x
+charx
, chary
, charwidth
);
221 static void decodeCharSet( unsigned char * mem
, unsigned char * charset
)
225 for( i
=0; i
<256; i
++ ) {
226 unsigned char * src
= mem
+ 16*i
;
227 unsigned char * dst
= charset
+ 64*i
;
229 decodeCharLine( src
, dst
, 0, 4, 8 );
230 decodeCharLine( src
+8, dst
, 0, 0, 8 );
234 static void decodeSprites( unsigned char * mem
, unsigned char * sprite_data
)
238 for( i
=0; i
<64; i
++ ) {
239 unsigned char * src
= mem
+ i
*64;
240 unsigned char * dst
= sprite_data
+ 256*i
;
242 decodeCharLine( src
, dst
, 8, 12, 16 );
243 decodeCharLine( src
+ 8, dst
, 8, 0, 16 );
244 decodeCharLine( src
+16, dst
, 8, 4, 16 );
245 decodeCharLine( src
+24, dst
, 8, 8, 16 );
246 decodeCharLine( src
+32, dst
, 0, 12, 16 );
247 decodeCharLine( src
+40, dst
, 0, 0, 16 );
248 decodeCharLine( src
+48, dst
, 0, 4, 16 );
249 decodeCharLine( src
+56, dst
, 0, 8, 16 );
254 Decode one byte from the encoded color palette.
256 An encoded palette byte contains RGB information bit-packed as follows:
259 color: b b g g g r r r
261 static unsigned decodePaletteByte( unsigned char value
)
263 unsigned bit0
, bit1
, bit2
;
264 unsigned red
, green
, blue
;
266 bit0
= (value
>> 0) & 0x01;
267 bit1
= (value
>> 1) & 0x01;
268 bit2
= (value
>> 2) & 0x01;
269 red
= 0x21 * bit0
+ 0x47 * bit1
+ 0x97 * bit2
;
271 bit0
= (value
>> 3) & 0x01;
272 bit1
= (value
>> 4) & 0x01;
273 bit2
= (value
>> 5) & 0x01;
274 green
= 0x21 * bit0
+ 0x47 * bit1
+ 0x97 * bit2
;
277 bit1
= (value
>> 6) & 0x01;
278 bit2
= (value
>> 7) & 0x01;
279 blue
= 0x21 * bit0
+ 0x47 * bit1
+ 0x97 * bit2
;
281 return (blue
<< 16 ) | (green
<< 8) | red
;
284 void decodeROMs(void)
286 unsigned decoded_palette
[0x20];
291 decodeCharSet( charset_rom_
, charmap_
);
292 decodeSprites( spriteset_rom_
, spritemap_
);
294 for( i
=0x00; i
<0x20; i
++ ) {
295 decoded_palette
[i
] = decodePaletteByte( palette_data_
[i
] );
297 for( i
=0; i
<256; i
++ ) {
298 c
= decoded_palette
[ color_data_
[i
] & 0x0F ];
299 #ifdef HAVE_LCD_COLOR
300 palette
[i
] = LCD_RGBPACK((unsigned char) (c
),
301 (unsigned char) (c
>> 8),
302 (unsigned char) (c
>> 16));
304 palette
[i
] = rgb_to_gray((unsigned char) (c
),
305 (unsigned char) (c
>> 8),
306 (unsigned char) (c
>> 16) );
311 void getDeviceInfo( enum InputDevice device
, unsigned char * mask
, unsigned char ** port
)
313 static unsigned char MaskInfo
[] = {
318 0x10 , // Switch_RackAdvance
321 0x80 , // Switch_AddCredit
326 0x10 , // Switch_Test
327 0x20 , // Key_OnePlayer
328 0x40 , // Key_TwoPlayers
329 0x80 // Switch_CocktailMode
332 *mask
= MaskInfo
[device
];
339 case Switch_RackAdvance
:
342 case Switch_AddCredit
:
352 case Switch_CocktailMode
:
361 enum InputDeviceMode
getDeviceMode( enum InputDevice device
)
364 unsigned char * port
;
366 getDeviceInfo( device
, &mask
, &port
);
368 return (*port
& mask
) == 0 ? DeviceOn
: DeviceOff
;
372 Fire an input event, telling the emulator for example
373 that the joystick has been released from the down position.
375 void setDeviceMode( enum InputDevice device
, enum InputDeviceMode mode
)
377 if( (getCoinLockout() == 0) && ((device
== CoinSlot_1
)||(device
== CoinSlot_2
)||(device
== Switch_AddCredit
)) ) {
378 // Coin slots are locked, ignore command and exit
383 unsigned char * port
;
385 getDeviceInfo( device
, &mask
, &port
);
387 if( mode
== DeviceOn
)
389 else if( mode
== DeviceOff
)
391 else if( mode
== DeviceToggle
)
395 void setDipSwitches( unsigned value
) {
396 dip_switches_
= (unsigned char) value
;
398 setDeviceMode( Switch_RackAdvance
, value
& DipRackAdvance_Auto
? DeviceOn
: DeviceOff
);
399 setDeviceMode( Switch_Test
, value
& DipMode_Test
? DeviceOn
: DeviceOff
);
400 setDeviceMode( Switch_CocktailMode
, value
& DipCabinet_Cocktail
? DeviceOn
: DeviceOff
);
403 unsigned getDipSwitches(void) {
404 unsigned result
= dip_switches_
;
406 if( getDeviceMode(Switch_RackAdvance
) == DeviceOn
) result
|= DipRackAdvance_Auto
;
407 if( getDeviceMode(Switch_Test
) == DeviceOn
) result
|= DipMode_Test
;
408 if( getDeviceMode(Switch_CocktailMode
) == DeviceOn
) result
|= DipCabinet_Cocktail
;
413 #if defined (CPU_COLDFIRE)
414 extern void drawChar( unsigned char * buffer
, int index
, int ox
, int oy
, int color
);
416 static inline void drawChar( unsigned char * buffer
, int index
, int ox
, int oy
, int color
)
420 /* Make the index point to the character offset into the character table */
421 unsigned char * chrmap
= charmap_
+ index
*64;
422 buffer
+= ox
+ oy
*224; /* Make the buffer point to the character position*/
423 color
= (color
& 0x3F)*4;
427 for( y
=7; y
>=0; y
-- )
429 rb
->memset( buffer
, 0, 8 );
430 buffer
+= ScreenWidth
;
435 if( output_devices_
& FlipScreen
) {
437 buffer
+= 7*ScreenWidth
;
438 for( y
=7; y
>=0; y
-- ) {
439 for( x
=7; x
>=0; x
-- ) {
440 *buffer
++ = (*chrmap
++) + color
;
442 buffer
-= ScreenWidth
+ 8; // Go to the next line
446 for( y
=7; y
>=0; y
-- ) {
447 for( x
=7; x
>=0; x
-- ) {
448 *buffer
++ = (*chrmap
++) + color
;
450 buffer
+= ScreenWidth
- 8; // Go to the next line
456 inline void drawSprite( unsigned char * buffer
, int index
)
458 struct PacmanSprite ps
= sprites_
[index
];
462 // Exit now if sprite not visible at all
463 if( (ps
.color
== 0) || (ps
.x
>= ScreenWidth
) || (ps
.y
< 16) || (ps
.y
>= (ScreenHeight
-32)) ) {
467 // Clip the sprite coordinates to cut the parts that fall off the screen
468 int start_x
= (ps
.x
< 0) ? 0 : ps
.x
;
469 int end_x
= (ps
.x
< (ScreenWidth
-16)) ? ps
.x
+15 : ScreenWidth
-1;
471 // Prepare variables for drawing
472 int color
= (ps
.color
& 0x3F)*4;
473 unsigned char * spritemap_base
= spritemap_
+ ((ps
.n
& 0x3F)*256);
475 buffer
+= ScreenWidth
*ps
.y
;
477 dirty_
[(start_x
>> 3) + (ps
.y
>> 3)*28] = 1;
478 dirty_
[(start_x
>> 3) + 1 + (ps
.y
>> 3)*28] = 1;
479 dirty_
[(end_x
>> 3) + (ps
.y
>> 3)*28] = 1;
480 dirty_
[(start_x
>> 3) + ((ps
.y
>> 3)+1)*28] = 1;
481 dirty_
[(start_x
>> 3) + 1 + ((ps
.y
>> 3)+1)*28] = 1;
482 dirty_
[(end_x
>> 3) + ((ps
.y
>> 3)+1)*28] = 1;
483 dirty_
[(start_x
>> 3) + ((ps
.y
+15) >> 3)*28] = 1;
484 dirty_
[(start_x
>> 3) + 1 + ((ps
.y
+15) >> 3)*28] = 1;
485 dirty_
[(end_x
>> 3) + ((ps
.y
+15) >> 3)*28] = 1;
487 // Draw the 16x16 sprite
488 if( ps
.mode
== 0 ) { // Normal
489 s2
= spritemap_base
+ start_x
-ps
.x
;
490 // Draw the 16x16 sprite
491 for( y
=15; y
>=0; y
-- ) {
493 for( x
=start_x
; x
<=end_x
; x
++, s
++ ) {
495 buffer
[x
] = color
+ *s
;
498 buffer
+= ScreenWidth
;
501 } else if( ps
.mode
== 1 ) { // Flip Y
502 s2
= spritemap_base
+ start_x
-ps
.x
+ 240;
503 for( y
=15; y
>=0; y
-- ) {
505 for( x
=start_x
; x
<=end_x
; x
++, s
++ ) {
507 buffer
[x
] = color
+ *s
;
510 buffer
+= ScreenWidth
;
513 } else if( ps
.mode
== 2 ) { // Flip X
514 s2
= spritemap_base
+ 15 + ps
.x
-start_x
;
515 for( y
=15; y
>=-0; y
-- ) {
517 for( x
=start_x
; x
<=end_x
; x
++, s
-- ) {
519 buffer
[x
] = color
+ *s
;
522 buffer
+= ScreenWidth
;
525 } else { // Flip X and Y
526 s2
= spritemap_base
+ 255 + ps
.x
-start_x
;
527 for( y
=15; y
>=0; y
-- ) {
529 for( x
=start_x
; x
<=end_x
; x
++, s
-- ) {
531 buffer
[x
] = color
+ *s
;
534 buffer
+= ScreenWidth
;
541 Draw the video into the specified buffer.
543 bool renderBackground( unsigned char * buffer
)
545 unsigned char * video
= video_mem_
;
546 unsigned char * color
= color_mem_
;
547 unsigned char * dirty
= dirty_
;
551 // Draw the background first...
552 if( output_devices_
& FlipScreen
) {
553 for( y
=ScreenHeight
-CharHeight
; y
>=0; y
-=CharHeight
) {
554 for( x
=ScreenWidth
-CharWidth
; x
>=0; x
-=CharWidth
) {
556 drawChar( buffer
, *video
++, x
, y
, *color
++ );
568 for( y
=0; y
<ScreenHeight
; y
+=CharHeight
) {
569 for( x
=0; x
<ScreenWidth
; x
+=CharWidth
) {
571 drawChar( buffer
, *video
++, x
, y
, *color
++ );
586 void renderSprites( unsigned char * buffer
)
590 // ...then add the sprites
591 for( i
=7; i
>=0; i
-- ) {
592 drawSprite( buffer
, i
);
596 /* Enables/disables the speed hack. */
597 int setSpeedHack( int enabled
)
602 if( (ram_
[0x180B] == 0xBE) && (ram_
[0x1FFD] == 0x00) ) {
603 // Patch the ROM to activate the speed hack
604 ram_
[0x180B] = 0x01; // Activate speed hack
605 ram_
[0x1FFD] = 0xBD; // Fix ROM checksum
611 if( (ram_
[0x180B] == 0x01) && (ram_
[0x1FFD] == 0xBD) ) {
612 // Restore the patched ROM locations