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 ****************************************************************************/
30 #include "pacbox_lcd.h"
32 #include "lib/configfile.h"
33 #include "lib/playback_control.h"
35 struct pacman_settings
{
44 static struct pacman_settings settings
;
45 static struct pacman_settings old_settings
;
46 static bool sound_playing
= false;
48 #define SETTINGS_VERSION 1
49 #define SETTINGS_MIN_VERSION 1
50 #define SETTINGS_FILENAME "pacbox.cfg"
52 static char* difficulty_options
[] = { "Normal", "Hard" };
53 static char* numlives_options
[] = { "1", "2", "3", "5" };
54 static char* bonus_options
[] = {"10000", "15000", "20000", "No Bonus"};
55 static char* ghostnames_options
[] = {"Normal", "Alternate"};
56 static char* yesno_options
[] = {"No", "Yes"};
58 static struct configdata config
[] =
60 {TYPE_ENUM
, 0, 2, { .int_p
= &settings
.difficulty
}, "Difficulty",
62 {TYPE_ENUM
, 0, 4, { .int_p
= &settings
.numlives
}, "Pacmen Per Game",
64 {TYPE_ENUM
, 0, 4, { .int_p
= &settings
.bonus
}, "Bonus", bonus_options
},
65 {TYPE_ENUM
, 0, 2, { .int_p
= &settings
.ghostnames
}, "Ghost Names",
67 {TYPE_ENUM
, 0, 2, { .int_p
= &settings
.showfps
}, "Show FPS",
69 {TYPE_ENUM
, 0, 2, { .int_p
= &settings
.sound
}, "Sound",
73 static bool loadFile( const char * name
, unsigned char * buf
, int len
)
75 char filename
[MAX_PATH
];
77 rb
->snprintf(filename
,sizeof(filename
), ROCKBOX_DIR
"/pacman/%s",name
);
79 int fd
= rb
->open( filename
, O_RDONLY
);
85 int n
= rb
->read( fd
, buf
, len
);
96 static bool loadROMS( void )
98 bool romsLoaded
= false;
100 romsLoaded
= loadFile( "pacman.6e", ram_
, 0x1000) &&
101 loadFile( "pacman.6f", ram_
+0x1000, 0x1000) &&
102 loadFile( "pacman.6h", ram_
+0x2000, 0x1000) &&
103 loadFile( "pacman.6j", ram_
+0x3000, 0x1000) &&
104 loadFile( "pacman.5e", charset_rom_
, 0x1000) &&
105 loadFile( "pacman.5f", spriteset_rom_
, 0x1000);
109 reset_PacmanMachine();
115 /* A buffer to render Pacman's 244x288 screen into */
116 static unsigned char video_buffer
[ScreenWidth
*ScreenHeight
] __attribute__ ((aligned (16)));
118 static long start_time
;
119 static long video_frames
= 0;
121 static int dipDifficulty
[] = { DipDifficulty_Normal
, DipDifficulty_Hard
};
122 static int dipLives
[] = { DipLives_1
, DipLives_2
, DipLives_3
, DipLives_5
};
123 static int dipBonus
[] = { DipBonus_10000
, DipBonus_15000
, DipBonus_20000
,
125 static int dipGhostNames
[] = { DipGhostNames_Normal
, DipGhostNames_Alternate
};
127 static int settings_to_dip(struct pacman_settings settings
)
129 return ( DipPlay_OneCoinOneGame
|
134 dipDifficulty
[settings
.difficulty
] |
135 dipLives
[settings
.numlives
] |
136 dipBonus
[settings
.bonus
] |
137 dipGhostNames
[settings
.ghostnames
]
141 static bool pacbox_menu(void)
147 bool need_restart
= false;
149 static const struct opt_items noyes
[2] = {
154 static const struct opt_items difficulty_options
[2] = {
159 static const struct opt_items numlives_options
[4] = {
166 static const struct opt_items bonus_options
[4] = {
167 { "10000 points", -1 },
168 { "15000 points", -1 },
169 { "20000 points", -1 },
173 static const struct opt_items ghostname_options
[2] = {
181 PBMI_PACMEN_PER_GAME
,
190 MENUITEM_STRINGLIST(menu
, "Pacbox Menu", NULL
,
191 "Difficulty", "Pacmen Per Game", "Bonus Life",
192 "Ghost Names", "Display FPS", "Sound",
195 rb
->button_clear_queue();
197 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
198 rb
->lcd_set_mode(LCD_MODE_RGB565
);
202 result
=rb
->do_menu(&menu
, &selected
, NULL
, false);
206 case PBMI_DIFFICULTY
:
207 new_setting
=settings
.difficulty
;
208 rb
->set_option("Difficulty", &new_setting
, INT
,
209 difficulty_options
, 2, NULL
);
210 if (new_setting
!= settings
.difficulty
) {
211 settings
.difficulty
=new_setting
;
215 case PBMI_PACMEN_PER_GAME
:
216 new_setting
=settings
.numlives
;
217 rb
->set_option("Pacmen Per Game", &new_setting
, INT
,
218 numlives_options
, 4, NULL
);
219 if (new_setting
!= settings
.numlives
) {
220 settings
.numlives
=new_setting
;
224 case PBMI_BONUS_LIFE
:
225 new_setting
=settings
.bonus
;
226 rb
->set_option("Bonus Life", &new_setting
, INT
,
227 bonus_options
, 4, NULL
);
228 if (new_setting
!= settings
.bonus
) {
229 settings
.bonus
=new_setting
;
233 case PBMI_GHOST_NAMES
:
234 new_setting
=settings
.ghostnames
;
235 rb
->set_option("Ghost Names", &new_setting
, INT
,
236 ghostname_options
, 2, NULL
);
237 if (new_setting
!= settings
.ghostnames
) {
238 settings
.ghostnames
=new_setting
;
242 case PBMI_DISPLAY_FPS
:
243 rb
->set_option("Display FPS",&settings
.showfps
,INT
,
247 rb
->set_option("Sound",&settings
.sound
, INT
,
260 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
261 rb
->lcd_set_mode(LCD_MODE_PAL256
);
265 init_PacmanMachine(settings_to_dip(settings
));
273 return (result
==PBMI_QUIT
);
276 /* Sound is emulated in ISR context, so not much is done per sound frame */
277 #define NBSAMPLES 128
278 static uint32_t sound_buf
[NBSAMPLES
];
279 #if CONFIG_CPU == MCF5249
280 /* Not enough to put this in IRAM */
281 static int16_t raw_buf
[NBSAMPLES
];
283 static int16_t raw_buf
[NBSAMPLES
] IBSS_ATTR
;
289 static void get_more(const void **start
, size_t *size
)
291 int32_t *out
, *outend
;
294 /* Emulate the audio for the current register settings */
295 playSound(raw_buf
, NBSAMPLES
);
298 outend
= out
+ NBSAMPLES
;
301 /* Convert to stereo */
304 uint32_t sample
= (uint16_t)*raw
++;
305 *out
++ = sample
| (sample
<< 16);
307 while (out
< outend
);
310 *size
= NBSAMPLES
*sizeof(sound_buf
[0]);
314 Start the sound emulation
316 static void start_sound(void)
323 #ifndef PLUGIN_USE_IRAM
324 /* Ensure control of PCM - stopping music isn't obligatory */
325 rb
->plugin_get_audio_buffer(NULL
);
328 /* Get the closest rate >= to what is preferred */
329 sr_index
= rb
->round_value_to_list32(PREFERRED_SAMPLING_RATE
,
330 rb
->hw_freq_sampr
, HW_NUM_FREQ
, false);
332 if (rb
->hw_freq_sampr
[sr_index
] < PREFERRED_SAMPLING_RATE
339 wsg3_set_sampling_rate(rb
->hw_freq_sampr
[sr_index
]);
341 rb
->pcm_set_frequency(rb
->hw_freq_sampr
[sr_index
]);
342 rb
->pcm_play_data(get_more
, NULL
, NULL
, 0);
344 sound_playing
= true;
348 Stop the sound emulation
350 static void stop_sound(void)
356 rb
->pcm_set_frequency(HW_SAMPR_DEFAULT
);
358 sound_playing
= false;
362 Runs the game engine for one frame.
364 static int gameProc( void )
369 int frame_counter
= 0;
370 int yield_counter
= 0;
377 /* Run the machine for one frame (1/60th second) */
382 /* Check the button status */
383 status
= rb
->button_status();
385 #ifdef HAS_BUTTON_HOLD
386 if (rb
->button_hold())
387 status
= PACMAN_MENU
;
390 if ((status
& PACMAN_MENU
) == PACMAN_MENU
391 #ifdef PACMAN_RC_MENU
392 || status
== PACMAN_RC_MENU
397 end_time
= *rb
->current_tick
;
401 menu_res
= pacbox_menu();
403 rb
->lcd_clear_display();
404 #ifdef HAVE_REMOTE_LCD
405 rb
->lcd_remote_clear_display();
406 rb
->lcd_remote_update();
414 start_time
+= *rb
->current_tick
-end_time
;
417 #ifdef PACMAN_HAS_REMOTE
418 setDeviceMode( Joy1_Left
, (status
& PACMAN_LEFT
|| status
== PACMAN_RC_LEFT
) ? DeviceOn
: DeviceOff
);
419 setDeviceMode( Joy1_Right
, (status
& PACMAN_RIGHT
|| status
== PACMAN_RC_RIGHT
) ? DeviceOn
: DeviceOff
);
420 setDeviceMode( Joy1_Up
, (status
& PACMAN_UP
|| status
== PACMAN_RC_UP
) ? DeviceOn
: DeviceOff
);
421 setDeviceMode( Joy1_Down
, (status
& PACMAN_DOWN
|| status
== PACMAN_RC_DOWN
) ? DeviceOn
: DeviceOff
);
422 setDeviceMode( CoinSlot_1
, (status
& PACMAN_COIN
|| status
== PACMAN_RC_COIN
) ? DeviceOn
: DeviceOff
);
423 setDeviceMode( Key_OnePlayer
, (status
& PACMAN_1UP
|| status
== PACMAN_RC_1UP
) ? DeviceOn
: DeviceOff
);
424 setDeviceMode( Key_TwoPlayers
, (status
& PACMAN_2UP
|| status
== PACMAN_RC_2UP
) ? DeviceOn
: DeviceOff
);
426 setDeviceMode( Joy1_Left
, (status
& PACMAN_LEFT
) ? DeviceOn
: DeviceOff
);
427 setDeviceMode( Joy1_Right
, (status
& PACMAN_RIGHT
) ? DeviceOn
: DeviceOff
);
428 setDeviceMode( Joy1_Up
, (status
& PACMAN_UP
) ? DeviceOn
: DeviceOff
);
429 setDeviceMode( Joy1_Down
, (status
& PACMAN_DOWN
) ? DeviceOn
: DeviceOff
);
430 setDeviceMode( CoinSlot_1
, (status
& PACMAN_COIN
) ? DeviceOn
: DeviceOff
);
431 setDeviceMode( Key_OnePlayer
, (status
& PACMAN_1UP
) ? DeviceOn
: DeviceOff
);
433 setDeviceMode( Key_TwoPlayers
, (status
& PACMAN_2UP
) ? DeviceOn
: DeviceOff
);
437 /* We only update the screen every third frame - Pacman's native
438 framerate is 60fps, so we are attempting to display 20fps */
439 if (frame_counter
== 60 / FPS
) {
446 if (yield_counter
== FPS
) {
451 /* The following functions render the Pacman screen from the
452 contents of the video and color ram. We first update the
453 background, and then draw the Sprites on top.
456 renderBackground( video_buffer
);
457 renderSprites( video_buffer
);
459 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
460 rb
->lcd_blit_pal256( video_buffer
, 0, 0, XOFS
, YOFS
,
461 ScreenWidth
, ScreenHeight
);
463 blit_display(rb
->lcd_framebuffer
,video_buffer
);
466 if (settings
.showfps
) {
467 fps
= (video_frames
*HZ
*100) / (*rb
->current_tick
-start_time
);
468 rb
->lcd_putsxyf(0,0,"%d.%02d / %d fps ",fps
/100,fps
%100,FPS
);
471 #if !defined(HAVE_LCD_MODES) || \
472 defined(HAVE_LCD_MODES) && !(HAVE_LCD_MODES & LCD_MODE_PAL256)
476 /* Keep the framerate at Pacman's 60fps */
477 end_time
= start_time
+ (video_frames
*HZ
)/FPS
;
478 while (TIME_BEFORE(*rb
->current_tick
,end_time
)) {
489 enum plugin_status
plugin_start(const void* parameter
)
493 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
496 rb
->lcd_set_backdrop(NULL
);
497 rb
->lcd_set_foreground(LCD_WHITE
);
498 rb
->lcd_set_background(LCD_BLACK
);
499 rb
->lcd_clear_display();
502 /* Set the default settings */
503 settings
.difficulty
= 0; /* Normal */
504 settings
.numlives
= 2; /* 3 lives */
505 settings
.bonus
= 0; /* 10000 points */
506 settings
.ghostnames
= 0; /* Normal names */
507 settings
.showfps
= 0; /* Do not show FPS */
508 settings
.sound
= 0; /* Sound off by default */
510 if (configfile_load(SETTINGS_FILENAME
, config
,
511 sizeof(config
)/sizeof(*config
),
515 /* If the loading failed, save a new config file (as the disk is
517 configfile_save(SETTINGS_FILENAME
, config
,
518 sizeof(config
)/sizeof(*config
),
522 /* Keep a copy of the saved version of the settings - so we can check if
523 the settings have changed when we quit */
524 old_settings
= settings
;
526 /* Initialise the hardware */
527 init_PacmanMachine(settings_to_dip(settings
));
529 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
530 rb
->lcd_set_mode(LCD_MODE_PAL256
);
533 /* Load the romset */
535 start_time
= *rb
->current_tick
-1;
539 /* Save the user settings if they have changed */
540 if (rb
->memcmp(&settings
,&old_settings
,sizeof(settings
))!=0) {
541 rb
->splash(0, "Saving settings...");
542 configfile_save(SETTINGS_FILENAME
, config
,
543 sizeof(config
)/sizeof(*config
),
547 rb
->splashf(HZ
*2, "No ROMs in %s/pacman/", ROCKBOX_DIR
);
550 #if defined(HAVE_LCD_MODES) && (HAVE_LCD_MODES & LCD_MODE_PAL256)
551 rb
->lcd_set_mode(LCD_MODE_RGB565
);
554 #ifdef HAVE_ADJUSTABLE_CPU_FREQ
555 rb
->cpu_boost(false);