FS#8961 - Anti-Aliased Fonts.
[kugel-rb.git] / apps / plugins / pacbox / arcade.c
blobd4fa0ada20d24fc7fd057eca24f57ac930fe6fcd
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
309 void getDeviceInfo( enum InputDevice device, unsigned char * mask, unsigned char ** port )
311 static unsigned char MaskInfo[] = {
312 0x01 , // Joy1_Up
313 0x02 , // Joy1_Left
314 0x04 , // Joy1_Right
315 0x08 , // Joy1_Down
316 0x10 , // Switch_RackAdvance
317 0x20 , // CoinSlot_1
318 0x40 , // CoinSlot_2
319 0x80 , // Switch_AddCredit
320 0x01 , // Joy2_Up
321 0x02 , // Joy2_Left
322 0x04 , // Joy2_Right
323 0x08 , // Joy2_Down
324 0x10 , // Switch_Test
325 0x20 , // Key_OnePlayer
326 0x40 , // Key_TwoPlayers
327 0x80 // Switch_CocktailMode
330 *mask = MaskInfo[device];
332 switch( device ) {
333 case Joy1_Up:
334 case Joy1_Left:
335 case Joy1_Right:
336 case Joy1_Down:
337 case Switch_RackAdvance:
338 case CoinSlot_1:
339 case CoinSlot_2:
340 case Switch_AddCredit:
341 *port = &port1_;
342 break;
343 case Joy2_Up:
344 case Joy2_Left:
345 case Joy2_Right:
346 case Joy2_Down:
347 case Switch_Test:
348 case Key_OnePlayer:
349 case Key_TwoPlayers:
350 case Switch_CocktailMode:
351 *port = &port2_;
352 break;
353 default:
354 *port = 0;
355 break;
359 enum InputDeviceMode getDeviceMode( enum InputDevice device )
361 unsigned char mask;
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
377 return;
380 unsigned char mask;
381 unsigned char * port;
383 getDeviceInfo( device, &mask, &port );
385 if( mode == DeviceOn )
386 *port &= ~mask;
387 else if( mode == DeviceOff )
388 *port |= mask;
389 else if( mode == DeviceToggle )
390 *port ^= mask;
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;
408 return result;
411 #if defined (CPU_COLDFIRE)
412 extern void drawChar( unsigned char * buffer, int index, int ox, int oy, int color );
413 #else
414 static inline void drawChar( unsigned char * buffer, int index, int ox, int oy, int color )
416 int x,y;
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;
423 if( color == 0 )
425 for( y=7; y>=0; y-- )
427 rb->memset( buffer, 0, 8 );
428 buffer += ScreenWidth;
430 return;
433 if( output_devices_ & FlipScreen ) {
434 // Flip character
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
443 else {
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
452 #endif
454 inline void drawSprite( unsigned char * buffer, int index )
456 struct PacmanSprite ps = sprites_[index];
457 int x,y;
458 char * s, * s2;
460 // Exit now if sprite not visible at all
461 if( (ps.color == 0) || (ps.x >= ScreenWidth) || (ps.y < 16) || (ps.y >= (ScreenHeight-32)) ) {
462 return;
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-- ) {
490 s = s2;
491 for( x=start_x; x<=end_x; x++, s++ ) {
492 if( *s ) {
493 buffer[x] = color + *s;
496 buffer += ScreenWidth;
497 s2 += 16;
499 } else if( ps.mode == 1 ) { // Flip Y
500 s2 = spritemap_base + start_x-ps.x + 240;
501 for( y=15; y>=0; y-- ) {
502 s = s2;
503 for( x=start_x; x<=end_x; x++, s++ ) {
504 if( *s ) {
505 buffer[x] = color + *s;
508 buffer += ScreenWidth;
509 s2 -= 16;
511 } else if( ps.mode == 2 ) { // Flip X
512 s2 = spritemap_base + 15 + ps.x-start_x;
513 for( y=15; y>=-0; y-- ) {
514 s = s2;
515 for( x=start_x; x<=end_x; x++, s-- ) {
516 if( *s ) {
517 buffer[x] = color + *s;
520 buffer += ScreenWidth;
521 s2 += 16;
523 } else { // Flip X and Y
524 s2 = spritemap_base + 255 + ps.x-start_x;
525 for( y=15; y>=0; y-- ) {
526 s = s2;
527 for( x=start_x; x<=end_x; x++, s-- ) {
528 if( *s ) {
529 buffer[x] = color + *s;
532 buffer += ScreenWidth;
533 s2 -= 16;
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_;
546 int x,y;
547 bool changed=false;
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 ) {
553 if (*dirty) {
554 drawChar( buffer, *video++, x, y, *color++ );
555 *(dirty++)=0;
556 changed=true;
557 } else {
558 dirty++;
559 video++;
560 color++;
565 else {
566 for( y=0; y<ScreenHeight; y+=CharHeight ) {
567 for( x=0; x<ScreenWidth; x+=CharWidth ) {
568 if (*dirty) {
569 drawChar( buffer, *video++, x, y, *color++ );
570 *(dirty++)=0;
571 changed=true;
572 } else {
573 dirty++;
574 video++;
575 color++;
581 return changed;
584 void renderSprites( unsigned char * buffer )
586 int i;
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 )
597 int result = 0;
599 if( 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
605 result = 1;
608 else {
609 if( (ram_[0x180B] == 0x01) && (ram_[0x1FFD] == 0xBD) ) {
610 // Restore the patched ROM locations
611 ram_[0x180B] = 0xBE;
612 ram_[0x1FFD] = 0x00;
614 result = 1;
618 return result;