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 #ifndef HAVE_LCD_COLOR
33 /* Convert RGB888 to 2-bit greyscale - logic taken from bmp2rb.c */
34 static fb_data
rgb_to_gray(unsigned int r
, unsigned int g
, unsigned int b
)
36 int brightness
= ( 2*r
+ 4*g
+ b
);
37 if( r
== 0 && g
== 0 && b
== 0 )
40 brightness
= (brightness
/450);
41 if( brightness
> 2 ) return 0;
42 else return 2-brightness
;
46 unsigned char color_data_
[256] = {
47 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0b, 0x01,
48 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0b, 0x03,
49 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0b, 0x05,
50 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0b, 0x07,
51 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x01, 0x09,
52 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
53 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
54 0x00, 0x0f, 0x00, 0x0e, 0x00, 0x01, 0x0c, 0x0f,
55 0x00, 0x0e, 0x00, 0x0b, 0x00, 0x0c, 0x0b, 0x0e,
56 0x00, 0x0c, 0x0f, 0x01, 0x00, 0x00, 0x00, 0x00,
57 0x00, 0x01, 0x02, 0x0f, 0x00, 0x07, 0x0c, 0x02,
58 0x00, 0x09, 0x06, 0x0f, 0x00, 0x0d, 0x0c, 0x0f,
59 0x00, 0x05, 0x03, 0x09, 0x00, 0x0f, 0x0b, 0x00,
60 0x00, 0x0e, 0x00, 0x0b, 0x00, 0x0e, 0x00, 0x0b,
61 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f, 0x0e, 0x01,
62 0x00, 0x0f, 0x0b, 0x0e, 0x00, 0x0e, 0x00, 0x0f,
63 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
64 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
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
81 unsigned char palette_data_
[0x20] = {
82 0x00, 0x07, 0x66, 0xef, 0x00, 0xf8, 0xea, 0x6f,
83 0x00, 0x3f, 0x00, 0xc9, 0x38, 0xaa, 0xaf, 0xf6,
84 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
85 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
95 /* Putting this in IRAM actually slows down the iPods, but is good for
99 fb_data palette
[256] IBSS_ATTR
; /* Color palette */
101 fb_data palette
[256]; /* Color palette */
105 void init_PacmanMachine(int dip
)
109 /* Initialize the CPU and the RAM */
111 rb
->memset( &ram_
[0x4000], 0xFF, 0x1000 );
113 /* Initialize parameters */
118 /* Reset the machine */
119 reset_PacmanMachine();
121 /* Set the DIP switches to a default configuration */
122 setDipSwitches( dip
);
124 /* Initialize the video character translation tables: video memory has a
125 very peculiar arrangement in Pacman so we precompute a few tables to
126 move around faster */
128 for( i
=0x000; i
<0x400; i
++ ) {
135 else if( i
>= 0x3C0 ) {
137 y
= ((i
-0x3C0) >> 5);
140 x
= 27 - ((i
-0x40) >> 5);
141 y
= 2 + ((i
-0x40) & 0x1F);
143 if( (y
>= 0) && (y
< 36) && (x
>= 0) && (x
< 28) )
144 vchar_to_i_
[i
] = y
*28 + x
;
146 vchar_to_i_
[i
] = 0x3FF;
150 void reset_PacmanMachine(void)
156 interrupt_vector_
= 0;
158 rb
->memset( ram_
+0x4000, 0, 0x1000 );
159 rb
->memset( color_mem_
, 0, sizeof(color_mem_
) );
160 rb
->memset( video_mem_
, 0, sizeof(video_mem_
) );
161 rb
->memset( dirty_
, 0, sizeof(dirty_
) );
163 for( i
=0; i
<8; i
++ ) {
164 sprites_
[i
].color
= 0;
165 sprites_
[i
].x
= ScreenWidth
;
170 Run the machine for one frame.
174 /* Run until the CPU has executed the number of cycles per frame
175 (the function returns the number of "extra" cycles spent by the
176 last instruction but that is not really important here) */
178 unsigned extraCycles
= z80_run( CpuCyclesPerFrame
);
180 /* Reset the CPU cycle counter to make sure it doesn't overflow,
181 also take into account the extra cycles from the previous run */
183 setCycles( extraCycles
);
185 /* If interrupts are enabled, force a CPU interrupt with the vector
186 set by the program */
188 if( output_devices_
& InterruptEnabled
) {
189 z80_interrupt( interrupt_vector_
);
195 /** Returns the status of the coin lockout door. */
196 unsigned char getCoinLockout(void) {
197 return output_devices_
& CoinLockout
? 1 : 0;
200 static void decodeCharByte( unsigned char b
, unsigned char * charbuf
, int charx
, int chary
, int charwidth
)
204 for( i
=3; i
>=0; i
-- ) {
205 charbuf
[charx
+(chary
+i
)*charwidth
] = (b
& 1) | ((b
>> 3) & 2);
210 static void decodeCharLine( unsigned char * src
, unsigned char * charbuf
, int charx
, int chary
, int charwidth
)
214 for( x
=7; x
>=0; x
-- ) {
215 decodeCharByte( *src
++, charbuf
, x
+charx
, chary
, charwidth
);
219 static void decodeCharSet( unsigned char * mem
, unsigned char * charset
)
223 for( i
=0; i
<256; i
++ ) {
224 unsigned char * src
= mem
+ 16*i
;
225 unsigned char * dst
= charset
+ 64*i
;
227 decodeCharLine( src
, dst
, 0, 4, 8 );
228 decodeCharLine( src
+8, dst
, 0, 0, 8 );
232 static void decodeSprites( unsigned char * mem
, unsigned char * sprite_data
)
236 for( i
=0; i
<64; i
++ ) {
237 unsigned char * src
= mem
+ i
*64;
238 unsigned char * dst
= sprite_data
+ 256*i
;
240 decodeCharLine( src
, dst
, 8, 12, 16 );
241 decodeCharLine( src
+ 8, dst
, 8, 0, 16 );
242 decodeCharLine( src
+16, dst
, 8, 4, 16 );
243 decodeCharLine( src
+24, dst
, 8, 8, 16 );
244 decodeCharLine( src
+32, dst
, 0, 12, 16 );
245 decodeCharLine( src
+40, dst
, 0, 0, 16 );
246 decodeCharLine( src
+48, dst
, 0, 4, 16 );
247 decodeCharLine( src
+56, dst
, 0, 8, 16 );
252 Decode one byte from the encoded color palette.
254 An encoded palette byte contains RGB information bit-packed as follows:
257 color: b b g g g r r r
259 static unsigned decodePaletteByte( unsigned char value
)
261 unsigned bit0
, bit1
, bit2
;
262 unsigned red
, green
, blue
;
264 bit0
= (value
>> 0) & 0x01;
265 bit1
= (value
>> 1) & 0x01;
266 bit2
= (value
>> 2) & 0x01;
267 red
= 0x21 * bit0
+ 0x47 * bit1
+ 0x97 * bit2
;
269 bit0
= (value
>> 3) & 0x01;
270 bit1
= (value
>> 4) & 0x01;
271 bit2
= (value
>> 5) & 0x01;
272 green
= 0x21 * bit0
+ 0x47 * bit1
+ 0x97 * bit2
;
275 bit1
= (value
>> 6) & 0x01;
276 bit2
= (value
>> 7) & 0x01;
277 blue
= 0x21 * bit0
+ 0x47 * bit1
+ 0x97 * bit2
;
279 return (blue
<< 16 ) | (green
<< 8) | red
;
282 void decodeROMs(void)
284 unsigned decoded_palette
[0x20];
289 decodeCharSet( charset_rom_
, charmap_
);
290 decodeSprites( spriteset_rom_
, spritemap_
);
292 for( i
=0x00; i
<0x20; i
++ ) {
293 decoded_palette
[i
] = decodePaletteByte( palette_data_
[i
] );
295 for( i
=0; i
<256; i
++ ) {
296 c
= decoded_palette
[ color_data_
[i
] & 0x0F ];
297 #ifdef HAVE_LCD_COLOR
298 palette
[i
] = LCD_RGBPACK((unsigned char) (c
),
299 (unsigned char) (c
>> 8),
300 (unsigned char) (c
>> 16));
302 palette
[i
] = rgb_to_gray((unsigned char) (c
),
303 (unsigned char) (c
>> 8),
304 (unsigned char) (c
>> 16) );
309 void getDeviceInfo( enum InputDevice device
, unsigned char * mask
, unsigned char ** port
)
311 static unsigned char MaskInfo
[] = {
316 0x10 , // Switch_RackAdvance
319 0x80 , // Switch_AddCredit
324 0x10 , // Switch_Test
325 0x20 , // Key_OnePlayer
326 0x40 , // Key_TwoPlayers
327 0x80 // Switch_CocktailMode
330 *mask
= MaskInfo
[device
];
337 case Switch_RackAdvance
:
340 case Switch_AddCredit
:
350 case Switch_CocktailMode
:
359 enum InputDeviceMode
getDeviceMode( enum InputDevice device
)
362 unsigned char * port
;
364 getDeviceInfo( device
, &mask
, &port
);
366 return (*port
& mask
) == 0 ? DeviceOn
: DeviceOff
;
370 Fire an input event, telling the emulator for example
371 that the joystick has been released from the down position.
373 void setDeviceMode( enum InputDevice device
, enum InputDeviceMode mode
)
375 if( (getCoinLockout() == 0) && ((device
== CoinSlot_1
)||(device
== CoinSlot_2
)||(device
== Switch_AddCredit
)) ) {
376 // Coin slots are locked, ignore command and exit
381 unsigned char * port
;
383 getDeviceInfo( device
, &mask
, &port
);
385 if( mode
== DeviceOn
)
387 else if( mode
== DeviceOff
)
389 else if( mode
== DeviceToggle
)
393 void setDipSwitches( unsigned value
) {
394 dip_switches_
= (unsigned char) value
;
396 setDeviceMode( Switch_RackAdvance
, value
& DipRackAdvance_Auto
? DeviceOn
: DeviceOff
);
397 setDeviceMode( Switch_Test
, value
& DipMode_Test
? DeviceOn
: DeviceOff
);
398 setDeviceMode( Switch_CocktailMode
, value
& DipCabinet_Cocktail
? DeviceOn
: DeviceOff
);
401 unsigned getDipSwitches(void) {
402 unsigned result
= dip_switches_
;
404 if( getDeviceMode(Switch_RackAdvance
) == DeviceOn
) result
|= DipRackAdvance_Auto
;
405 if( getDeviceMode(Switch_Test
) == DeviceOn
) result
|= DipMode_Test
;
406 if( getDeviceMode(Switch_CocktailMode
) == DeviceOn
) result
|= DipCabinet_Cocktail
;
411 #if defined (CPU_COLDFIRE)
412 extern void drawChar( unsigned char * buffer
, int index
, int ox
, int oy
, int color
);
414 static inline void drawChar( unsigned char * buffer
, int index
, int ox
, int oy
, int color
)
418 /* Make the index point to the character offset into the character table */
419 unsigned char * chrmap
= charmap_
+ index
*64;
420 buffer
+= ox
+ oy
*224; /* Make the buffer point to the character position*/
421 color
= (color
& 0x3F)*4;
425 for( y
=7; y
>=0; y
-- )
427 rb
->memset( buffer
, 0, 8 );
428 buffer
+= ScreenWidth
;
433 if( output_devices_
& FlipScreen
) {
435 buffer
+= 7*ScreenWidth
;
436 for( y
=7; y
>=0; y
-- ) {
437 for( x
=7; x
>=0; x
-- ) {
438 *buffer
++ = (*chrmap
++) + color
;
440 buffer
-= ScreenWidth
+ 8; // Go to the next line
444 for( y
=7; y
>=0; y
-- ) {
445 for( x
=7; x
>=0; x
-- ) {
446 *buffer
++ = (*chrmap
++) + color
;
448 buffer
+= ScreenWidth
- 8; // Go to the next line
454 inline void drawSprite( unsigned char * buffer
, int index
)
456 struct PacmanSprite ps
= sprites_
[index
];
460 // Exit now if sprite not visible at all
461 if( (ps
.color
== 0) || (ps
.x
>= ScreenWidth
) || (ps
.y
< 16) || (ps
.y
>= (ScreenHeight
-32)) ) {
465 // Clip the sprite coordinates to cut the parts that fall off the screen
466 int start_x
= (ps
.x
< 0) ? 0 : ps
.x
;
467 int end_x
= (ps
.x
< (ScreenWidth
-16)) ? ps
.x
+15 : ScreenWidth
-1;
469 // Prepare variables for drawing
470 int color
= (ps
.color
& 0x3F)*4;
471 unsigned char * spritemap_base
= spritemap_
+ ((ps
.n
& 0x3F)*256);
473 buffer
+= ScreenWidth
*ps
.y
;
475 dirty_
[(start_x
>> 3) + (ps
.y
>> 3)*28] = 1;
476 dirty_
[(start_x
>> 3) + 1 + (ps
.y
>> 3)*28] = 1;
477 dirty_
[(end_x
>> 3) + (ps
.y
>> 3)*28] = 1;
478 dirty_
[(start_x
>> 3) + ((ps
.y
>> 3)+1)*28] = 1;
479 dirty_
[(start_x
>> 3) + 1 + ((ps
.y
>> 3)+1)*28] = 1;
480 dirty_
[(end_x
>> 3) + ((ps
.y
>> 3)+1)*28] = 1;
481 dirty_
[(start_x
>> 3) + ((ps
.y
+15) >> 3)*28] = 1;
482 dirty_
[(start_x
>> 3) + 1 + ((ps
.y
+15) >> 3)*28] = 1;
483 dirty_
[(end_x
>> 3) + ((ps
.y
+15) >> 3)*28] = 1;
485 // Draw the 16x16 sprite
486 if( ps
.mode
== 0 ) { // Normal
487 s2
= spritemap_base
+ start_x
-ps
.x
;
488 // Draw the 16x16 sprite
489 for( y
=15; y
>=0; y
-- ) {
491 for( x
=start_x
; x
<=end_x
; x
++, s
++ ) {
493 buffer
[x
] = color
+ *s
;
496 buffer
+= ScreenWidth
;
499 } else if( ps
.mode
== 1 ) { // Flip Y
500 s2
= spritemap_base
+ start_x
-ps
.x
+ 240;
501 for( y
=15; y
>=0; y
-- ) {
503 for( x
=start_x
; x
<=end_x
; x
++, s
++ ) {
505 buffer
[x
] = color
+ *s
;
508 buffer
+= ScreenWidth
;
511 } else if( ps
.mode
== 2 ) { // Flip X
512 s2
= spritemap_base
+ 15 + ps
.x
-start_x
;
513 for( y
=15; y
>=-0; y
-- ) {
515 for( x
=start_x
; x
<=end_x
; x
++, s
-- ) {
517 buffer
[x
] = color
+ *s
;
520 buffer
+= ScreenWidth
;
523 } else { // Flip X and Y
524 s2
= spritemap_base
+ 255 + ps
.x
-start_x
;
525 for( y
=15; y
>=0; y
-- ) {
527 for( x
=start_x
; x
<=end_x
; x
++, s
-- ) {
529 buffer
[x
] = color
+ *s
;
532 buffer
+= ScreenWidth
;
539 Draw the video into the specified buffer.
541 bool renderBackground( unsigned char * buffer
)
543 unsigned char * video
= video_mem_
;
544 unsigned char * color
= color_mem_
;
545 unsigned char * dirty
= dirty_
;
549 // Draw the background first...
550 if( output_devices_
& FlipScreen
) {
551 for( y
=ScreenHeight
-CharHeight
; y
>=0; y
-=CharHeight
) {
552 for( x
=ScreenWidth
-CharWidth
; x
>=0; x
-=CharWidth
) {
554 drawChar( buffer
, *video
++, x
, y
, *color
++ );
566 for( y
=0; y
<ScreenHeight
; y
+=CharHeight
) {
567 for( x
=0; x
<ScreenWidth
; x
+=CharWidth
) {
569 drawChar( buffer
, *video
++, x
, y
, *color
++ );
584 void renderSprites( unsigned char * buffer
)
588 // ...then add the sprites
589 for( i
=7; i
>=0; i
-- ) {
590 drawSprite( buffer
, i
);
594 /* Enables/disables the speed hack. */
595 int setSpeedHack( int enabled
)
600 if( (ram_
[0x180B] == 0xBE) && (ram_
[0x1FFD] == 0x00) ) {
601 // Patch the ROM to activate the speed hack
602 ram_
[0x180B] = 0x01; // Activate speed hack
603 ram_
[0x1FFD] = 0xBD; // Fix ROM checksum
609 if( (ram_
[0x180B] == 0x01) && (ram_
[0x1FFD] == 0xBD) ) {
610 // Restore the patched ROM locations