Update the discussion of themeing in the manual, and put a note in the wps tags appen...
[kugel-rb.git] / apps / plugins / pacbox / arcade.c
blobb8a0f105bbdf5570056c2c4abe34a46c14536d58
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 #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 )
38 return 3;
40 brightness = (brightness/450);
41 if( brightness > 2 ) return 0;
42 else return 2-brightness;
44 #endif
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
88 enum {
89 Normal = 0x00,
90 FlipY = 0x01,
91 FlipX = 0x02,
92 FlipXY = 0x03
95 /* Putting this in IRAM actually slows down the iPods, but is good for
96 the Coldfire
98 #ifdef CPU_COLDFIRE
99 fb_data palette[256] IBSS_ATTR; /* Color palette */
100 #else
101 fb_data palette[256]; /* Color palette */
102 #endif
105 void init_PacmanMachine(int dip)
107 int i;
109 /* Initialize the CPU and the RAM */
110 z80_reset();
111 rb->memset( &ram_[0x4000], 0xFF, 0x1000 );
113 /* Initialize parameters */
114 port1_ = 0xFF;
115 port2_ = 0xFF;
116 coin_counter_ = 0;
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++ ) {
129 int x, y;
131 if( i < 0x040 ) {
132 x = 29 - (i & 0x1F);
133 y = 34 + (i >> 5);
135 else if( i >= 0x3C0 ) {
136 x = 29 - (i & 0x1F);
137 y = ((i-0x3C0) >> 5);
139 else {
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;
145 else
146 vchar_to_i_[i] = 0x3FF;
150 void reset_PacmanMachine(void)
152 int i;
154 z80_reset();
155 output_devices_ = 0;
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.
172 int run(void)
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_ );
192 return 0;
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 )
202 int i;
204 for( i=3; i>=0; i-- ) {
205 charbuf[charx+(chary+i)*charwidth] = (b & 1) | ((b >> 3) & 2);
206 b >>= 1;
210 static void decodeCharLine( unsigned char * src, unsigned char * charbuf, int charx, int chary, int charwidth )
212 int x;
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 )
221 int i;
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 )
234 int i;
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:
256 bit: 7 6 5 4 3 2 1 0
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;
274 bit0 = 0;
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];
285 unsigned c;
287 int i;
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));
301 #else
302 palette[i] = rgb_to_gray((unsigned char) (c),
303 (unsigned char) (c >> 8),
304 (unsigned char) (c >> 16) );
305 #endif
308 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
309 rb->lcd_pal256_update_pal(palette);
310 #endif
313 void getDeviceInfo( enum InputDevice device, unsigned char * mask, unsigned char ** port )
315 static unsigned char MaskInfo[] = {
316 0x01 , // Joy1_Up
317 0x02 , // Joy1_Left
318 0x04 , // Joy1_Right
319 0x08 , // Joy1_Down
320 0x10 , // Switch_RackAdvance
321 0x20 , // CoinSlot_1
322 0x40 , // CoinSlot_2
323 0x80 , // Switch_AddCredit
324 0x01 , // Joy2_Up
325 0x02 , // Joy2_Left
326 0x04 , // Joy2_Right
327 0x08 , // Joy2_Down
328 0x10 , // Switch_Test
329 0x20 , // Key_OnePlayer
330 0x40 , // Key_TwoPlayers
331 0x80 // Switch_CocktailMode
334 *mask = MaskInfo[device];
336 switch( device ) {
337 case Joy1_Up:
338 case Joy1_Left:
339 case Joy1_Right:
340 case Joy1_Down:
341 case Switch_RackAdvance:
342 case CoinSlot_1:
343 case CoinSlot_2:
344 case Switch_AddCredit:
345 *port = &port1_;
346 break;
347 case Joy2_Up:
348 case Joy2_Left:
349 case Joy2_Right:
350 case Joy2_Down:
351 case Switch_Test:
352 case Key_OnePlayer:
353 case Key_TwoPlayers:
354 case Switch_CocktailMode:
355 *port = &port2_;
356 break;
357 default:
358 *port = 0;
359 break;
363 enum InputDeviceMode getDeviceMode( enum InputDevice device )
365 unsigned char mask;
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
381 return;
384 unsigned char mask;
385 unsigned char * port;
387 getDeviceInfo( device, &mask, &port );
389 if( mode == DeviceOn )
390 *port &= ~mask;
391 else if( mode == DeviceOff )
392 *port |= mask;
393 else if( mode == DeviceToggle )
394 *port ^= mask;
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;
412 return result;
415 #if defined (CPU_COLDFIRE)
416 extern void drawChar( unsigned char * buffer, int index, int ox, int oy, int color );
417 #else
418 static inline void drawChar( unsigned char * buffer, int index, int ox, int oy, int color )
420 int x,y;
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;
427 if( color == 0 )
429 for( y=7; y>=0; y-- )
431 rb->memset( buffer, 0, 8 );
432 buffer += ScreenWidth;
434 return;
437 if( output_devices_ & FlipScreen ) {
438 // Flip character
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
447 else {
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
456 #endif
458 inline void drawSprite( unsigned char * buffer, int index )
460 struct PacmanSprite ps = sprites_[index];
461 int x,y;
462 char * s, * s2;
464 // Exit now if sprite not visible at all
465 if( (ps.color == 0) || (ps.x >= ScreenWidth) || (ps.y < 16) || (ps.y >= (ScreenHeight-32)) ) {
466 return;
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-- ) {
494 s = s2;
495 for( x=start_x; x<=end_x; x++, s++ ) {
496 if( *s ) {
497 buffer[x] = color + *s;
500 buffer += ScreenWidth;
501 s2 += 16;
503 } else if( ps.mode == 1 ) { // Flip Y
504 s2 = spritemap_base + start_x-ps.x + 240;
505 for( y=15; y>=0; y-- ) {
506 s = s2;
507 for( x=start_x; x<=end_x; x++, s++ ) {
508 if( *s ) {
509 buffer[x] = color + *s;
512 buffer += ScreenWidth;
513 s2 -= 16;
515 } else if( ps.mode == 2 ) { // Flip X
516 s2 = spritemap_base + 15 + ps.x-start_x;
517 for( y=15; y>=-0; y-- ) {
518 s = s2;
519 for( x=start_x; x<=end_x; x++, s-- ) {
520 if( *s ) {
521 buffer[x] = color + *s;
524 buffer += ScreenWidth;
525 s2 += 16;
527 } else { // Flip X and Y
528 s2 = spritemap_base + 255 + ps.x-start_x;
529 for( y=15; y>=0; y-- ) {
530 s = s2;
531 for( x=start_x; x<=end_x; x++, s-- ) {
532 if( *s ) {
533 buffer[x] = color + *s;
536 buffer += ScreenWidth;
537 s2 -= 16;
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_;
550 int x,y;
551 bool changed=false;
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 ) {
557 if (*dirty) {
558 drawChar( buffer, *video++, x, y, *color++ );
559 *(dirty++)=0;
560 changed=true;
561 } else {
562 dirty++;
563 video++;
564 color++;
569 else {
570 for( y=0; y<ScreenHeight; y+=CharHeight ) {
571 for( x=0; x<ScreenWidth; x+=CharWidth ) {
572 if (*dirty) {
573 drawChar( buffer, *video++, x, y, *color++ );
574 *(dirty++)=0;
575 changed=true;
576 } else {
577 dirty++;
578 video++;
579 color++;
585 return changed;
588 void renderSprites( unsigned char * buffer )
590 int i;
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 )
601 int result = 0;
603 if( 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
609 result = 1;
612 else {
613 if( (ram_[0x180B] == 0x01) && (ram_[0x1FFD] == 0xBD) ) {
614 // Restore the patched ROM locations
615 ram_[0x180B] = 0xBE;
616 ram_[0x1FFD] = 0x00;
618 result = 1;
622 return result;