Thursday 28 April 2016

Misc. configuration

We now have all the bricks for our lovely gaming system. A little tweaking and configuration, and everything will be perfect :-)

Configure startup resolution

For display on a HD TV, I recommend a 1280x720 resolution, which is more than enough for all the nice pixels you will display, low enough not to make the BeagleBone suffer, and standard enough to ensure that you will be able to actually have something on screen!

Edit your /boot/uboot/uEnv.txt file and simply edit/add the kms_force_mode variable as follows:
 kms_force_mode=video=HDMI-A-1:1280x720@60e  

Configure system startup

We now want to make sure that the BeagleBone Blacks starts the GPIO handling process and the GUI automatically.

The environment variables and the device tree overlay are set up with the file /etc/profile.d/pixbox.sh, as follows:
 #!/bin/bash  
 export LD_LIBRARY_PATH=/media/BBB/lib:  
 export PATH=/media/BBB/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin  
 export SLOTS=/sys/devices/bone_capemgr.9/slots  
 export PINS=/sys/kernel/debug/pinctrl/44e10800.pinmux/pins  
 export CFLAGS="-O3 -march=armv7-a -mtune=cortex-a8 -mfloat-abi=hard -mfpu=vfpv3-d16 -ffast-math"  
 export CXXFLAGS="$CFLAGS"  
 export SDL_VIDEODRIVER=fbcon  
 echo PixBox > $SLOTS  
Don't forget to make it executable (chmod a+x /etc/profile.d/pixbox.sh).

To start the GUI automatically, create the file /etc/init.d/pixbox.sh (make it executable too):
 #!/bin/bash  
 # The following part carries out specific functions depending on arguments.  
 case "$1" in  
  start)  
     if [ `cat /sys/class/net/eth0/carrier` -eq "0" ]  
     then  
         source /etc/profile.d/pixbox.sh  
         export USER=root  
         export SHELL=/bin/bash  
         export PWD=/media/BBB/pixbox/pixbox42  
         export HOME=/root  
         export NO_MENU=1  
         /media/BBB/pixbox/service/pixIo_mmap &  
         /media/BBB/pixbox/pixbox42/pixbox_gui  
     fi  
   ;;  
  stop)  
   ;;  
  *)  
   exit 1  
   ;;  
 esac  
 exit 0  
Very simple pseudo-service, but with a nice twist: we check that no carrier if found on eth0 to start the GUI. This makes things simpler for remote connection to the device: if plugged at startup, we are in "normal Linux/maintenance" mode, no GUI is started. Otherwise, start GUI.

By the way, feel free to tweak the network setup scripts to reduce startup delay (no DHCP if you wish - see /etc/networks/interfaces - , or reduce DHCP timeout - see /etc/dhcp/dhclient.conf).

Final words

This should cover most of my work, feel free to contact me if you have specific questions or if I have been too quick on some info. I will probably edit and enrich my articles as I continue fiddling with my system!

Cheers!

A custom launcher

I know that lots of emulator front-ends already exist, but designing my own was part of the fun of the project :-) My aims were to :
  • Have a nice-looking, pixel-based, GUI
  • Be able to filter games by number of players / type of game / machine
  • Have a simple database, with no need for a network connection for updates
  • Discover the SDL library

GUI

Once again, I did not intend to share it at the beginning of this project, but I do it nonetheless for the few curious ones. I made a few modifications for sharing (made some parameters easier to change from within a .h header rather than hard-coded), but this is far from being the level of quality of an actual product (few comments, some stuff is still hard-coded, and lots of other things).

Anyway, I am quite happy with the final look and feel.

You can get the source code on my github account.

Here are some details for those who wish to adapt the code for their own use:
  • Compile with build.sh. Modifying it should be straightforward if you have already compiled something with gcc.
  • I have exposed a few macros in defines.h that you may want to adapt:
    • PIXBOX_WIDTH and PIXBOX_HEIGHT define the screen size. Set it to your own resolution (mine in 1280x720, adapted to TVs, and much faster than 1920x1080)
    • NUM_GAMES is the maximum number of games exposed at once
    • RESOURCE_PATH is the path to the files containing the resources used by the GUI: list of games ("database"), pictures, sounds.
    • TEXT_*: the texts to display. Mine are in French, but you can easily change them
  • You can adapt the positions of the elements, the colors etc in GraphicElements.cpp, in GraphicElements::Private::Private().
  • You can adapt the keys used in the GUI by editing PixBox.cpp (void PixBox::mainLoop()), check all the "case SDLK_*". Yes, you'll see mentions to a third player, but that's another project :-)
  • It also compiles and runs nicely on Windows. This could come in handy, I used it for most of the development.

Games database

As for the database, this is quite simple, actually. It is a csv file (comma-separated values), each column being one kind of info. To make things simpler, I also introduced a system of macros, to make things easier to read.
A sample is provided in the resources folder on the GUI repository.
The file is read line by line
  • If the line starts with PREPARE, this is a command to run at startup. e.g.
    • PREPARE;cp /media/BBB/pixbox/config/config2.cfg /root/.picodrive/;
  • If the line starts with MACRO, this is a macro, a replacement for another string. e.g.
    • MACRO;<GG>;advmess gamegear -cart
    • When calling "<GG> foo.gg", the command "advmess gamegear -cart foo.gg" will be executed. The macros are concatenated, don't forget to add space characters where needed!
  • If the line starts with "#", this is a comment, the line is ignored
  • Otherwise, the lines are read field by field as follows:
    • Field 1: Full game name
    • Field 2: Short game name (for display in the games list)
    • Field 3: Sorting key. This may be different from the title (e.g. you may want to display "Ultimate Mortal Kombat 3" next to "Mortal Kombat 3", just give them keys such as "mortal-kombat-3" and "mortal-kombat-3-u")
    • Field 4: Type of game. Any string, that will be used to sort the game in the "type" area of the GUI
    • Field 5: Machine. Any string,  that will be used to sort the game in the "machine" area of the GUI
    • Field 6: Max number of players: 1, 2, 3
    • Field 7: Game family. Any string, that will be used to sort the game in the "family" area of the GUI (think "Batman", "Disney", "Shinobi", etc.)
    • Field 8: Artwork: Path to an image to display, if any. There may be glitches if no artwork provided (wrong artwork displayed)
    • Field 9: Command line to start the game

Finally, the "database" must be in Unix text format, with lines ending with "\n", and NOT "\r\n" (see wikipedia for more info on this matter). If you edit your file on Windows, think of passing it to dos2unix (available on cygwin or MobaXterm) before sending it to your BeagleBone Black.

Wednesday 27 April 2016

Misc. emulation: AdvanceMess

For the last two systems I wished to emulate (NES, Game Boy and Game Gear), I decided to use the AdvanceMess "all-in-one" system, as AdvanceMame was easy to use on the BeagleBone Black. Not much to say here, just get the advancemess-0.102.0.1 source tarball available here, untar it, and compile it as you would any program. Just make sure to enable the proper drivers (SDL and FrameBuffer in our case):
  ./configure --prefix=/media/BBB/ --enable-sdl --enable-fb   
  make   
  make install   

NES and Game Gear emulation work fine, but the Game Gear screen appears blue-ish. I might spend a little time in the future investigating this, but for what I have seen, the code is a bit too... messy... for a quick fix!
I did not manage to get PCEngine emulation to work, I may take a little time too to investigate this. The games just do not start, no obvious error.

To start a game with advancemess, you have to drop the rom in a subfolder of your roms directory named after your system. For example, if you set your dir_rom folder as /media/BBB/roms in your ~/.advance/advmess.rc configuration file, drop your "Mega Man (USA).nes" rom in the /media/BBB/roms/nes/ folder, and start the emulation with
 advmess nes -cart "Mega Man (USA).nes"  

As a final note on this matter, here is my advmess.rc file. Nothing really specific.
 debug_crash no  
 debug_rawsound no  
 debug_speedmark no  
 device_color_bgr15 yes  
 device_color_bgr16 yes  
 device_color_bgr24 yes  
 device_color_bgr32 yes  
 device_color_bgr8 yes  
 device_color_palette8 yes  
 device_color_yuy2 yes  
 device_joystick auto  
 device_keyboard sdl  
 device_mouse auto  
 device_raw_firstkeyhack no  
 device_raw_mousedev[0] auto  
 device_raw_mousedev[1] auto  
 device_raw_mousedev[2] auto  
 device_raw_mousedev[3] auto  
 device_raw_mousetype[0] pnp  
 device_raw_mousetype[1] pnp  
 device_raw_mousetype[2] pnp  
 device_raw_mousetype[3] pnp  
 device_sdl_samples 512  
 device_sound oss  
 device_video sdl  
 device_video_cursor auto  
 device_video_doublescan yes  
 device_video_fastchange no  
 device_video_interlace yes  
 device_video_output auto  
 device_video_overlaysize 1024  
 device_video_singlescan yes  
 dir_artwork /root/.advance/artwork:/media/BBB//share/advance/artwork  
 dir_crc /root/.advance/crc  
 dir_diff /root/.advance/diff  
 dir_hi /root/.advance/hi  
 dir_image /root/.advance/image:/media/BBB//share/advance/image:/media/BBB/roms  
 dir_inp /root/.advance/inp  
 dir_memcard /root/.advance/memcard  
 dir_nvram /root/.advance/nvram  
 dir_rom /root/.advance/rom:/media/BBB//share/advance/rom:/media/BBB/roms/  
 dir_sample /root/.advance/sample:/media/BBB//share/advance/sample  
 dir_snap /root/.advance/snap  
 dir_sta /root/.advance/sta  
 display_adjust none  
 display_antialias no  
 display_artwork_backdrop yes  
 display_artwork_bezel no  
 display_artwork_crop yes  
 display_artwork_overlay yes  
 display_aspectx 16  
 display_aspecty 9  
 display_beam 1  
 display_brightness 1  
 display_buffer no  
 display_color auto  
 display_expand 1  
 display_flicker 0  
 display_flipx no  
 display_flipy no  
 display_frameskip auto  
 display_gamma 1  
 display_intensity 1.5  
 display_interlaceeffect none  
 display_magnify 1  
 display_mode auto  
 display_pausebrightness 1  
 display_resize mixed  
 display_resizeeffect none  
 display_restore yes  
 display_rgbeffect none  
 display_rol no  
 display_ror no  
 display_scanlines no  
 display_skipcolumns auto  
 display_skiplines auto  
 display_translucency yes  
 display_vsync yes  
 include  
 input_hotkey yes  
 input_idleexit 0  
 input_map[p1_up] keyboard[0,up]  
 input_map[p1_left] keyboard[0,left]  
 input_map[p1_right] keyboard[0,right]  
 input_map[p1_down] keyboard[0,down]  
 input_map[p1_button1] keyboard[0,a]  
 input_map[p1_button2] keyboard[0,s]  
 input_map[p1_select] keyboard[0,w]  
 input_map[p2_up] keyboard[0,i]  
 input_map[p2_left] keyboard[0,j]  
 input_map[p2_right] keyboard[0,l]  
 input_map[p2_down] keyboard[0,k]  
 input_map[p2_button1] keyboard[0,f]  
 input_map[p2_button2] keyboard[0,g]  
 input_map[p2_select] keyboard[0,t]  
 input_steadykey no  
 lcd_server none  
 lcd_speed 4  
 lcd_timeout 500  
 misc_bios default  
 misc_cheat no  
 misc_cheatfile cheat.dat  
 misc_difficulty none  
 misc_eventdebug no  
 misc_eventfile event.dat  
 misc_freeplay no  
 misc_hiscorefile hiscore.dat  
 misc_lang none  
 misc_languagefile english.lng  
 misc_mutedemo no  
 misc_quiet yes  
 misc_ramsize auto  
 misc_safequit no  
 misc_smp no  
 misc_timetorun 0  
 record_sound yes  
 record_sound_time 15  
 record_video yes  
 record_video_interleave 2  
 record_video_time 15  
 script_coin1  
 script_coin2  
 script_coin3  
 script_coin4  
 script_emulation  
 script_event1  
 script_event10  
 script_event11  
 script_event12  
 script_event13  
 script_event14  
 script_event2  
 script_event3  
 script_event4  
 script_event5  
 script_event6  
 script_event7  
 script_event8  
 script_event9  
 script_led1 on(kdb, 0b1); wait(!event()); off(kdb, 0b1);  
 script_led2 on(kdb, 0b10); wait(!event()); off(kdb, 0b10);  
 script_led3  
 script_play  
 script_safequit  
 script_start1  
 script_start2  
 script_start3  
 script_start4  
 script_turbo while (event()) { toggle(kdb, 0b100); delay(100); } off(kdb, 0b100);  
 script_video wait(!event()); set(kdb, 0);  
 sound_adjust auto  
 sound_equalizer_highvolume 0  
 sound_equalizer_lowvolume 0  
 sound_equalizer_midvolume 0  
 sound_latency 0.05  
 sound_mode mono  
 sound_normalize yes  
 sound_samplerate 22050  
 sound_samples yes  
 sound_volume -3  
 sync_fps auto  
 sync_resample auto  
 sync_speed 1  
 sync_startuptime auto  
 sync_turbospeed 3  
 ui_color[help_other] 000000 808080  
 ui_color[help_p1] 000000 ffff00  
 ui_color[help_p2] 000000 00ff00  
 ui_color[help_p3] 000000 ff0000  
 ui_color[help_p4] 000000 00ffff  
 ui_color[interface] 000000 ffffff  
 ui_color[select] 000000 afffff  
 ui_color[tag] 247ef0 ffffff  
 ui_font auto  
 ui_fontsize auto  
 ui_helpimage auto  
 ui_translucency 0.8