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) );
308 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
309 rb
->lcd_pal256_update_pal(palette
);
313 void getDeviceInfo( enum InputDevice device
, unsigned char * mask
, unsigned char ** port
)
315 static unsigned char MaskInfo
[] = {
320 0x10 , // Switch_RackAdvance
323 0x80 , // Switch_AddCredit
328 0x10 , // Switch_Test
329 0x20 , // Key_OnePlayer
330 0x40 , // Key_TwoPlayers
331 0x80 // Switch_CocktailMode
334 *mask
= MaskInfo
[device
];
341 case Switch_RackAdvance
:
344 case Switch_AddCredit
:
354 case Switch_CocktailMode
:
363 enum InputDeviceMode
getDeviceMode( enum InputDevice device
)
366 unsigned char * port
;
368 getDeviceInfo( device
, &mask
, &port
);
370 return (*port
& mask
) == 0 ? DeviceOn
: DeviceOff
;
374 Fire an input event, telling the emulator for example
375 that the joystick has been released from the down position.
377 void setDeviceMode( enum InputDevice device
, enum InputDeviceMode mode
)
379 if( (getCoinLockout() == 0) && ((device
== CoinSlot_1
)||(device
== CoinSlot_2
)||(device
== Switch_AddCredit
)) ) {
380 // Coin slots are locked, ignore command and exit
385 unsigned char * port
;
387 getDeviceInfo( device
, &mask
, &port
);
389 if( mode
== DeviceOn
)
391 else if( mode
== DeviceOff
)
393 else if( mode
== DeviceToggle
)
397 void setDipSwitches( unsigned value
) {
398 dip_switches_
= (unsigned char) value
;
400 setDeviceMode( Switch_RackAdvance
, value
& DipRackAdvance_Auto
? DeviceOn
: DeviceOff
);
401 setDeviceMode( Switch_Test
, value
& DipMode_Test
? DeviceOn
: DeviceOff
);
402 setDeviceMode( Switch_CocktailMode
, value
& DipCabinet_Cocktail
? DeviceOn
: DeviceOff
);
405 unsigned getDipSwitches(void) {
406 unsigned result
= dip_switches_
;
408 if( getDeviceMode(Switch_RackAdvance
) == DeviceOn
) result
|= DipRackAdvance_Auto
;
409 if( getDeviceMode(Switch_Test
) == DeviceOn
) result
|= DipMode_Test
;
410 if( getDeviceMode(Switch_CocktailMode
) == DeviceOn
) result
|= DipCabinet_Cocktail
;
415 #if defined (CPU_COLDFIRE)
416 extern void drawChar( unsigned char * buffer
, int index
, int ox
, int oy
, int color
);
418 static inline void drawChar( unsigned char * buffer
, int index
, int ox
, int oy
, int color
)
422 /* Make the index point to the character offset into the character table */
423 unsigned char * chrmap
= charmap_
+ index
*64;
424 buffer
+= ox
+ oy
*224; /* Make the buffer point to the character position*/
425 color
= (color
& 0x3F)*4;
429 for( y
=7; y
>=0; y
-- )
431 rb
->memset( buffer
, 0, 8 );
432 buffer
+= ScreenWidth
;
437 if( output_devices_
& FlipScreen
) {
439 buffer
+= 7*ScreenWidth
;
440 for( y
=7; y
>=0; y
-- ) {
441 for( x
=7; x
>=0; x
-- ) {
442 *buffer
++ = (*chrmap
++) + color
;
444 buffer
-= ScreenWidth
+ 8; // Go to the next line
448 for( y
=7; y
>=0; y
-- ) {
449 for( x
=7; x
>=0; x
-- ) {
450 *buffer
++ = (*chrmap
++) + color
;
452 buffer
+= ScreenWidth
- 8; // Go to the next line
458 inline void drawSprite( unsigned char * buffer
, int index
)
460 struct PacmanSprite ps
= sprites_
[index
];
464 // Exit now if sprite not visible at all
465 if( (ps
.color
== 0) || (ps
.x
>= ScreenWidth
) || (ps
.y
< 16) || (ps
.y
>= (ScreenHeight
-32)) ) {
469 // Clip the sprite coordinates to cut the parts that fall off the screen
470 int start_x
= (ps
.x
< 0) ? 0 : ps
.x
;
471 int end_x
= (ps
.x
< (ScreenWidth
-16)) ? ps
.x
+15 : ScreenWidth
-1;
473 // Prepare variables for drawing
474 int color
= (ps
.color
& 0x3F)*4;
475 unsigned char * spritemap_base
= spritemap_
+ ((ps
.n
& 0x3F)*256);
477 buffer
+= ScreenWidth
*ps
.y
;
479 dirty_
[(start_x
>> 3) + (ps
.y
>> 3)*28] = 1;
480 dirty_
[(start_x
>> 3) + 1 + (ps
.y
>> 3)*28] = 1;
481 dirty_
[(end_x
>> 3) + (ps
.y
>> 3)*28] = 1;
482 dirty_
[(start_x
>> 3) + ((ps
.y
>> 3)+1)*28] = 1;
483 dirty_
[(start_x
>> 3) + 1 + ((ps
.y
>> 3)+1)*28] = 1;
484 dirty_
[(end_x
>> 3) + ((ps
.y
>> 3)+1)*28] = 1;
485 dirty_
[(start_x
>> 3) + ((ps
.y
+15) >> 3)*28] = 1;
486 dirty_
[(start_x
>> 3) + 1 + ((ps
.y
+15) >> 3)*28] = 1;
487 dirty_
[(end_x
>> 3) + ((ps
.y
+15) >> 3)*28] = 1;
489 // Draw the 16x16 sprite
490 if( ps
.mode
== 0 ) { // Normal
491 s2
= spritemap_base
+ start_x
-ps
.x
;
492 // Draw the 16x16 sprite
493 for( y
=15; y
>=0; y
-- ) {
495 for( x
=start_x
; x
<=end_x
; x
++, s
++ ) {
497 buffer
[x
] = color
+ *s
;
500 buffer
+= ScreenWidth
;
503 } else if( ps
.mode
== 1 ) { // Flip Y
504 s2
= spritemap_base
+ start_x
-ps
.x
+ 240;
505 for( y
=15; y
>=0; y
-- ) {
507 for( x
=start_x
; x
<=end_x
; x
++, s
++ ) {
509 buffer
[x
] = color
+ *s
;
512 buffer
+= ScreenWidth
;
515 } else if( ps
.mode
== 2 ) { // Flip X
516 s2
= spritemap_base
+ 15 + ps
.x
-start_x
;
517 for( y
=15; y
>=-0; y
-- ) {
519 for( x
=start_x
; x
<=end_x
; x
++, s
-- ) {
521 buffer
[x
] = color
+ *s
;
524 buffer
+= ScreenWidth
;
527 } else { // Flip X and Y
528 s2
= spritemap_base
+ 255 + ps
.x
-start_x
;
529 for( y
=15; y
>=0; y
-- ) {
531 for( x
=start_x
; x
<=end_x
; x
++, s
-- ) {
533 buffer
[x
] = color
+ *s
;
536 buffer
+= ScreenWidth
;
543 Draw the video into the specified buffer.
545 bool renderBackground( unsigned char * buffer
)
547 unsigned char * video
= video_mem_
;
548 unsigned char * color
= color_mem_
;
549 unsigned char * dirty
= dirty_
;
553 // Draw the background first...
554 if( output_devices_
& FlipScreen
) {
555 for( y
=ScreenHeight
-CharHeight
; y
>=0; y
-=CharHeight
) {
556 for( x
=ScreenWidth
-CharWidth
; x
>=0; x
-=CharWidth
) {
558 drawChar( buffer
, *video
++, x
, y
, *color
++ );
570 for( y
=0; y
<ScreenHeight
; y
+=CharHeight
) {
571 for( x
=0; x
<ScreenWidth
; x
+=CharWidth
) {
573 drawChar( buffer
, *video
++, x
, y
, *color
++ );
588 void renderSprites( unsigned char * buffer
)
592 // ...then add the sprites
593 for( i
=7; i
>=0; i
-- ) {
594 drawSprite( buffer
, i
);
598 /* Enables/disables the speed hack. */
599 int setSpeedHack( int enabled
)
604 if( (ram_
[0x180B] == 0xBE) && (ram_
[0x1FFD] == 0x00) ) {
605 // Patch the ROM to activate the speed hack
606 ram_
[0x180B] = 0x01; // Activate speed hack
607 ram_
[0x1FFD] = 0xBD; // Fix ROM checksum
613 if( (ram_
[0x180B] == 0x01) && (ram_
[0x1FFD] == 0xBD) ) {
614 // Restore the patched ROM locations