Accept patch in FS#9637 by Keith Perri, fixing a crash when loading a cfg which speci...
[kugel-rb.git] / apps / plugins / pacbox / arcade.c
blobd5c73993a8f55627d047b1f488deef4fa877a419
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
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 ****************************************************************************/
27 #include "arcade.h"
28 #include "hardware.h"
29 #include <string.h>
30 #include "plugin.h"
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 )
40 return 3;
42 brightness = (brightness/450);
43 if( brightness > 2 ) return 0;
44 else return 2-brightness;
46 #endif
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
90 enum {
91 Normal = 0x00,
92 FlipY = 0x01,
93 FlipX = 0x02,
94 FlipXY = 0x03
97 /* Putting this in IRAM actually slows down the iPods, but is good for
98 the Coldfire
100 #ifdef CPU_COLDFIRE
101 fb_data palette[256] IBSS_ATTR; /* Color palette */
102 #else
103 fb_data palette[256]; /* Color palette */
104 #endif
107 void init_PacmanMachine(int dip)
109 int i;
111 /* Initialize the CPU and the RAM */
112 z80_reset();
113 rb->memset( &ram_[0x4000], 0xFF, 0x1000 );
115 /* Initialize parameters */
116 port1_ = 0xFF;
117 port2_ = 0xFF;
118 coin_counter_ = 0;
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++ ) {
131 int x, y;
133 if( i < 0x040 ) {
134 x = 29 - (i & 0x1F);
135 y = 34 + (i >> 5);
137 else if( i >= 0x3C0 ) {
138 x = 29 - (i & 0x1F);
139 y = ((i-0x3C0) >> 5);
141 else {
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;
147 else
148 vchar_to_i_[i] = 0x3FF;
152 void reset_PacmanMachine(void)
154 int i;
156 z80_reset();
157 output_devices_ = 0;
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.
174 int run(void)
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_ );
194 return 0;
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 )
204 int i;
206 for( i=3; i>=0; i-- ) {
207 charbuf[charx+(chary+i)*charwidth] = (b & 1) | ((b >> 3) & 2);
208 b >>= 1;
212 static void decodeCharLine( unsigned char * src, unsigned char * charbuf, int charx, int chary, int charwidth )
214 int x;
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 )
223 int i;
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 )
236 int i;
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:
258 bit: 7 6 5 4 3 2 1 0
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;
276 bit0 = 0;
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];
287 unsigned c;
289 int i;
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));
303 #else
304 palette[i] = rgb_to_gray((unsigned char) (c),
305 (unsigned char) (c >> 8),
306 (unsigned char) (c >> 16) );
307 #endif
311 void getDeviceInfo( enum InputDevice device, unsigned char * mask, unsigned char ** port )
313 static unsigned char MaskInfo[] = {
314 0x01 , // Joy1_Up
315 0x02 , // Joy1_Left
316 0x04 , // Joy1_Right
317 0x08 , // Joy1_Down
318 0x10 , // Switch_RackAdvance
319 0x20 , // CoinSlot_1
320 0x40 , // CoinSlot_2
321 0x80 , // Switch_AddCredit
322 0x01 , // Joy2_Up
323 0x02 , // Joy2_Left
324 0x04 , // Joy2_Right
325 0x08 , // Joy2_Down
326 0x10 , // Switch_Test
327 0x20 , // Key_OnePlayer
328 0x40 , // Key_TwoPlayers
329 0x80 // Switch_CocktailMode
332 *mask = MaskInfo[device];
334 switch( device ) {
335 case Joy1_Up:
336 case Joy1_Left:
337 case Joy1_Right:
338 case Joy1_Down:
339 case Switch_RackAdvance:
340 case CoinSlot_1:
341 case CoinSlot_2:
342 case Switch_AddCredit:
343 *port = &port1_;
344 break;
345 case Joy2_Up:
346 case Joy2_Left:
347 case Joy2_Right:
348 case Joy2_Down:
349 case Switch_Test:
350 case Key_OnePlayer:
351 case Key_TwoPlayers:
352 case Switch_CocktailMode:
353 *port = &port2_;
354 break;
355 default:
356 *port = 0;
357 break;
361 enum InputDeviceMode getDeviceMode( enum InputDevice device )
363 unsigned char mask;
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
379 return;
382 unsigned char mask;
383 unsigned char * port;
385 getDeviceInfo( device, &mask, &port );
387 if( mode == DeviceOn )
388 *port &= ~mask;
389 else if( mode == DeviceOff )
390 *port |= mask;
391 else if( mode == DeviceToggle )
392 *port ^= mask;
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;
410 return result;
413 #if defined (CPU_COLDFIRE)
414 extern void drawChar( unsigned char * buffer, int index, int ox, int oy, int color );
415 #else
416 static inline void drawChar( unsigned char * buffer, int index, int ox, int oy, int color )
418 int x,y;
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;
425 if( color == 0 )
427 for( y=7; y>=0; y-- )
429 rb->memset( buffer, 0, 8 );
430 buffer += ScreenWidth;
432 return;
435 if( output_devices_ & FlipScreen ) {
436 // Flip character
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
445 else {
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
454 #endif
456 inline void drawSprite( unsigned char * buffer, int index )
458 struct PacmanSprite ps = sprites_[index];
459 int x,y;
460 char * s, * s2;
462 // Exit now if sprite not visible at all
463 if( (ps.color == 0) || (ps.x >= ScreenWidth) || (ps.y < 16) || (ps.y >= (ScreenHeight-32)) ) {
464 return;
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-- ) {
492 s = s2;
493 for( x=start_x; x<=end_x; x++, s++ ) {
494 if( *s ) {
495 buffer[x] = color + *s;
498 buffer += ScreenWidth;
499 s2 += 16;
501 } else if( ps.mode == 1 ) { // Flip Y
502 s2 = spritemap_base + start_x-ps.x + 240;
503 for( y=15; y>=0; y-- ) {
504 s = s2;
505 for( x=start_x; x<=end_x; x++, s++ ) {
506 if( *s ) {
507 buffer[x] = color + *s;
510 buffer += ScreenWidth;
511 s2 -= 16;
513 } else if( ps.mode == 2 ) { // Flip X
514 s2 = spritemap_base + 15 + ps.x-start_x;
515 for( y=15; y>=-0; y-- ) {
516 s = s2;
517 for( x=start_x; x<=end_x; x++, s-- ) {
518 if( *s ) {
519 buffer[x] = color + *s;
522 buffer += ScreenWidth;
523 s2 += 16;
525 } else { // Flip X and Y
526 s2 = spritemap_base + 255 + ps.x-start_x;
527 for( y=15; y>=0; y-- ) {
528 s = s2;
529 for( x=start_x; x<=end_x; x++, s-- ) {
530 if( *s ) {
531 buffer[x] = color + *s;
534 buffer += ScreenWidth;
535 s2 -= 16;
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_;
548 int x,y;
549 bool changed=false;
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 ) {
555 if (*dirty) {
556 drawChar( buffer, *video++, x, y, *color++ );
557 *(dirty++)=0;
558 changed=true;
559 } else {
560 dirty++;
561 video++;
562 color++;
567 else {
568 for( y=0; y<ScreenHeight; y+=CharHeight ) {
569 for( x=0; x<ScreenWidth; x+=CharWidth ) {
570 if (*dirty) {
571 drawChar( buffer, *video++, x, y, *color++ );
572 *(dirty++)=0;
573 changed=true;
574 } else {
575 dirty++;
576 video++;
577 color++;
583 return changed;
586 void renderSprites( unsigned char * buffer )
588 int i;
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 )
599 int result = 0;
601 if( 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
607 result = 1;
610 else {
611 if( (ram_[0x180B] == 0x01) && (ram_[0x1FFD] == 0xBD) ) {
612 // Restore the patched ROM locations
613 ram_[0x180B] = 0xBE;
614 ram_[0x1FFD] = 0x00;
616 result = 1;
620 return result;