Sunday 29 May 2016

PCEngine emulation: Hu-Go!

This one has been suggested by a reader of this blog (actually, the only one I got feedback from, and very interesting discussions :-)). It's been a little tricky to get to work, and at this time I am still struggling with sound, but I believe this article can be of interest to anyone trying to compile it on ARM.

Hu-Go is quite an old emulator from Zeograd (https://www.zeograd.com/hugo_download.php), which has been forked into Huexpress. But as Huexpress seems to require OpenGL, I preferred to put my hopes into Hu-Go!

Compilation of Hu-Go has been a pain: actual C issues, no out-of-the-box way to add include paths, and wrong detection of the platform leading to code not being compiled and resulting in link errors... I suggest you get the modified version I have put on my github account: https://github.com/alban-rochel/pixbox-hugo. This one:

  • Fixes compilation issues, if you follow the instructions carefully
  • Is already set up for a 1280x720 display. Otherwise, edit osd_sdl_gfx.c, lines 250 and 251, and put in whatever you want!
To be able to compile Hu-Go! on a Pixbox-like platform, where the custom SDL library is not in a standard place, you have to trick it to add the SDL include path to its search paths (by adding it to CFLAGS). Which may not be immediate for non gcc-savvy geeks. And more, you have to get into the guts of the configure script to understand why it cannot tell you are compiling on a Linux platform. Actually, it looks for an x86 Linux platform, but has nothing x86 specific! So we'll tell it to compile on an x86 platform!

So I ended up with the following compilation line:
 CFLAGS="$CFLAGS -I/media/BBB/include/SDL" ./configure --prefix=/media/BBB --disable-gui --disable-netplay --with-sdl --with-sdl-mixer --with-sdl-prefix=/media/BBB/lib/ --with-sdl-exec-prefix=/media/BBB --build=i386-linux --target=i386-linux  
 make install  

And that's it!

Actually, this is still Work In Progress. I still have to map the controls to the Pixbox keys (nothing frightening), and, most of all, understand why the sound is so bad! I'll update this page when I have made progress on these subjects.

To start a game:
 /media/BBB/bin/hugo /media/BBB/roms/pce/Bonk\ III\ -\ Bonk\'s\ Big\ Adventure\ \(USA\).pce -s -w 2  
The -w argument is important as it triggers the "windowed" mode (even though this makes no sense in framebuffer display), which actually takes the resolution you have passed in osd_sdl_gfx.c!

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  

Wednesday 2 March 2016

Playstation emulation: pcsx-rearmed

When I started this project, I was far from imagining that I could even try to emulate the Playstation. But I gave a try at pcsx-rearmed, and I was astonished! Even without hardware acceleration, lots of games actually work quite smoothly!

The original code is available here: https://github.com/notaz/pcsx_rearmed

I have modified it for my usage, but my version may not be up to date. My patches are:

  • Modification of the configure script to enable the right options for the BeagleBone Black platform with SDL
  • Made the base save/configuration path absolute (and hard-coded) rather than relative. I want the path to stay the same even when starting the application from a service (my launcher)
  • Removed the menu when setting the NO_MENU environment variable: the only menu I want is that of my launcher
  • Removed a few "if"s that were never used in my case (show fps etc.). That's a pathology some developers like me get when getting in other people's codes - they tend to leave their marks for the sake of "optimization" or any other stupid reason :-)
  • Modification of the internal libpicofe, so that the environment variable PCSX_WIDTH, when set, allows forcing a size for the display. Some games do not run smoothly full-screen, so I decided to be able to reduce their size rather than ditch them.
The source code for my fork is available here: https://github.com/alban-rochel/pixbox-pcsx-rearmed

To compile:
 ./configure  
 make  

And to run, for example:

 PCSX_WIDTH=600 NO_MENU=1 /media/BBB/sources/pcsx_rearmed/pcsx -cdfile /media/BBB/roms/psx/MyGame.cue  

Arcade emulation: AdvanceMame

This one will be very quick: just get AdvanceMame 1.2 and compile :-)

OK, I'll give more details! First of all, get the tarball right here: https://sourceforge.net/projects/advancemame/files/advancemame/1.2/

Compilation is straightforward (when you're used to the Linux world), just check the configure options to activate the proper flags. We want to display on the frame buffer, and use SDL for the rest, so:
 ./configure --prefix=/media/BBB/ --enable-sdl --enable-fb  
 make  
 make install  

The "toughest" part is to configure Mame to run and display the way you want. This is done by experimenting with the $HOME/.advance/advmame.rc file: specify the paths to your roms, the input mapping, the display settings, etc.

Here is mine, for information:
 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 none  
 device_keyboard sdl  
 device_mouse none  
 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 auto  
 device_video_singlescan yes  
 dir_artwork /root/.advance/artwork:/media/BBB/share/advance/artwork  
 dir_diff /root/.advance/diff  
 dir_hi /root/.advance/hi  
 dir_image /root/.advance/image:/media/BBB/share/advance/image  
 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  
 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_magnifysize 640  
 display_mode auto  
 display_pausebrightness 1  
 display_resize fractional  
 display_resizeeffect auto  
 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_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_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 auto  
 sound_normalize yes  
 sound_samplerate 44100  
 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  
 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_button3] keyboard[0,d]  
 input_map[p1_button4] keyboard[0,q]  
 input_map[p1_button5] keyboard[0,w]  
 input_map[p1_button6] keyboard[0,e]  
 input_map[p1_button7] keyboard[0,z]  
 input_map[p1_button8] keyboard[0,x]  
 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_button3] keyboard[0,h]  
 input_map[p2_button4] keyboard[0,r]  
 input_map[p2_button5] keyboard[0,t]  
 input_map[p2_button6] keyboard[0,y]  
 input_map[p2_button7] keyboard[0,v]  
 input_map[p2_button8] keyboard[0,b]  

Starting a game is "easy" once your paths are properly configured:
 advmame game_name  

You just have to "guess" your game name, but it is generally the name of your ROM, or very close, and Mame offers you suggestions if it fails at recognizing the game.

Super NES emulation: snes9x-sdl

The SNES emulation on snes9x-sdl has been one of my disappointments on this project. Although some games are playable, I frequenty encounter huge slowdowns that I have not been able to fix. The strange thing is that, as the slowdowns appear suddenly in the middle of a game, they seem to be triggered by some event that I have not been able to identify.

You can find the original source code right here: https://github.com/domaemon/snes9x-sdl

Anyway, I might take some time in the future to work on this, but in the meantime, you might be interested by some tweaks that I have done in snes9x-sdl.

  • I have made a minor optimization in the 2x upscale filter, and written a 3x upscale filter (file filter/blit.cpp). This enables quasi-fullscreen on a 720p display. The calls to this filter have been hard-coded, along with other optimizations, in sdl/sdlvideo.cpp
  • I have hard-coded my key setup in sdl/sdlinput.cpp. I've done that 2 years ago, so I don't remember exactly why this was needed, but it must have been convenient :-)
  • I have changed an audio rate setting in sdl/sdlmain.cpp. This one has been a pain to fix. I did not have sound at first, and through debugging, I noticed that this value was the culprit. Changing it made everything fine afterward.

My own version of the emulator is on my github page: https://github.com/alban-rochel/pixbox-snes9x-sdl. The pixbox.patch file at the root lists all the changes I have made.

To compile, make sure the following environment variables are set, and gcc/g++ are the 4.9.1 versions (not sure this is very important in practice):
 CC = gcc  
 CXX = g++  
 AS = as  
 CFLAGS += -O3 -march=armv7-a -mtune=cortex-a8 -mfloat-abi=hard -mfpu=vfpv3-d16   
 LD_LIBRARY_PATH=/media/BBB/lib  

/media/BBB/lib is the path where the SDL libraries have been installed. And then, just compile, in the classical Linux way:
 ./configure --prefix=/media/BBB/bin --enable-mtune=cortex-a8 --disable-gamepad
 make  

You can now run the emulator as follows (adapt to your own path):
 /media/BBB/sources/snes9x-sdl-master-optim/sdl/snes9x-sdl -nomp5 -nojustifier -nomouse -nosuperscope -port1 pad1 -port2 pad2 -playbackrate 40960 /media/BBB/roms/snes/your_rom  

Tuesday 1 March 2016

Megadrive/Genesis, Master System, MegaCD/SegaCD, 32x emulation: Picodrive

I love old Sega systems! I know, Nintendo had Mario, Zelda, etc. are awesome, but one cannot fight childhood memories!

Picodrive is an awesome emulator: it emulates the Megadrive ecosystem, and even the Master System (which can be considered as part of the Megadrive ecosystem, as there was actually a Master System in each Megadrive). 32x emulation is way too slow on my setup, but it works. I remember seeing people running it on other, roughly equivalent systems, so I may have missed something.

I have put on my github account my modified version of Picodrive. The "pixbox.patch" file at the root of the folder is the difference with the original repository. It has been a long time since I last checked it out, and there must have been some evolutions, but I do not want to risk breaking what works perfectly on my system.

So, here are my main changes anyway:

  • I have forced "6 button" input on all the controllers. This was to fix what I supposed was a bug (some buttons not working), but which was a wrong soldering of my buttons! I suppose you can feel free to ignore this patch. I recall that I did not intend to share my modifications at first!
  • I have hard-coded the path to the system roms to /root/.picodrive. Otherwise, it looked for them in the current directory, which made little sense for an application which I intended to start from a service.
  • The most important change: starting the application with the environment variable NO_MENU set removes the menu when quitting a game. Which allows returning to my custom launcher.
Make sure the following environment variables are set, and gcc/g++ are the 4.9.1 versions (not sure this is very important in practice):
 CC = gcc  
 CXX = g++  
 AS = as  
 CFLAGS += -O3 -march=armv7-a -mtune=cortex-a8 -mfloat-abi=hard -mfpu=vfpv3-d16   
 LD_LIBRARY_PATH=/media/BBB/lib  

/media/BBB/lib is the path where the SDL libraries have been installed. And then, just compile, in the classical Linux way:
 ./configure --sound-drivers=sdl  
 make  

Once compiled, you can move the executable wherever you want (I put mine in /media/BBB/bin and set my PATH environment variable accordingly).

My typical command line to start the emulator is:
 SDL_VIDEO_YUV_DIRECT=1 SDL_VIDEO_YUV_HWACCEL=1 SDL_ACCEL=1 LD_LIBRARY_PATH=${LD_LIBRARY_PATH}:/media/BBB/lib NO_MENU=1 /media/BBB/sources/picodrive/picodrive/PicoDrive -config /root/.picodrive/config2.cfg   

Yes, that's a lot of environment variables! But this is necessary to have a proper display on SDL. Feel free to tweak your own config2.cfg file, it is quite self-explanatory (or you can start the emulator without NO_MENU set to configure it once and for all).

NeoGeo emulation: gngeo

I have very little to say about this emulator: it runs almost out-of-the-box on the BeagleBone Black. I use version 0.7 (unmodified), which you can get here: https://sourceforge.net/projects/gngeo.berlios/files/.

Make sure the following environment variables are set, and gcc/g++ are the 4.9.1 versions (not sure this is very important in practice):
 CC = gcc  
 CXX = g++  
 AS = as  
 CFLAGS += -O3 -march=armv7-a -mtune=cortex-a8 -mfloat-abi=hard -mfpu=vfpv3-d16   
 LD_LIBRARY_PATH=/media/BBB/lib  
/media/BBB/lib is the path where the SDL libraries have been installed. And then, just compile, in the classical Linux way:
 ./configure --prefix=/media/BBB/ --program-suffix=-0.7 --disable-gui
make  
make install

The only quirk with this library is that it sometimes fails at loading roms, as the romset names may not match what you have. I have edited my romset definitions to match those of the games I have. I made it public on my github repository, in case this can help: https://github.com/alban-rochel/pixbox-gngeo-romset-definitions Not all games are covered, but this may help you discover Metal Slug, which is the essential part of any emulation system!

To start the emulator, here is the command line I use, which allows me to set the path to the roms, the path to my romset definitions, the scaling (3x, full height on a 720p display), and the key mapping:
 gngeo-0.7 --p1key=97,115,100,113,49,53,273,274,276,275 --p2key=102,103,104,98,50,116,105,107,106,108 --scale=3 --romrcdir=/media/BBB/sources/gngeo-0.7/romrc.d/ --rompath=/media/BBB/roms/neogeo/   

Saturday 27 February 2016

Turning the Beaglebone Black into a joystick controller

In my opinion, this is the most interesting part of the project. And the biggest challenge for me.

First of all, have a look at your Beaglebone Black board. So many GPIO ports waiting idlely for some arcade fun! One of the advantages of the Beaglebone Black board over boards like the Raspberry Pi is that lots of GPIO ports are available, more than we need actually if we want to go the simple way: 1 button - 1 GPIO. And as a total beginner in electronics, I wanted to go the simple way!

I wanted to provide a 2-player arcade system (2x 4-way controllers), with enough buttons to emulate up to the Playstation generation (2x 8 action buttons, 2x start buttons), 1 button to "add coins into the slot", and 1 "exit button". 28 "buttons".

Setting up the GPIOs


We have to tell the Beaglebone Black that we will use 28 GPIOs as inputs, activating their internal "pulldown" resistors. This is done by setting a specific device-tree overlay, that tells the system how to configure its ports.
This is explained better than I could by Derek Molloy: http://derekmolloy.ie/gpios-on-the-beaglebone-black-using-device-tree-overlays/. Go check his work, it's great. He doesn't know it, but he deserves a large part of the credit in my Pixbox project! My controller setup is just an application of this tutorial and the related ones.

These documents he made are essential for building your setup: https://github.com/derekmolloy/boneDeviceTree/tree/master/docs
Print them, everything you will ever need is inside.

Anyway, here is the GPIO setup I ended up using:
Button GPIO Key event emitted
Escape (leave game)P8_8Escape
Insert coinP9_155
1P Joystick UpP8_10Up arrow
1P Joystick LeftP8_14Left arrow
1P Joystick RightP9_16Right arrow
1P Joystick DownP8_12Down arrow
1P StartP8_71
1P Action 1P8_9A
1P Action 2P8_13S
1P Action 3P8_17D
1P Action 4P8_26Q
1P Action 5P8_11W
1P Action 6P8_15E
1P Action 7P8_19Z
1P Action 8P9_26X
2P Joystick UpP9_27I
2P Joystick LeftP9_23J
2P Joystick RightP9_27L
2P Joystick DownP9_21K
2P StartP9_112
2P Action 1P9_13F
2P Action 2P9_14G
2P Action 3P9_18H
2P Action 4P9_24B
2P Action 5P9_12T
2P Action 6P9_16Y
2P Action 7P9_22V
2P Action 8P9_30R
The device tree overlay part only activates the GPIOs. The key mapping comes in a later step! Youd can get my device tree overlay definition on the following git repo: https://github.com/alban-rochel/pixbox-controls. Check the "overlay" subfolder. The compilation script also does the installation into the /lib/firmware folder. To activate this overlay definition, you have to tell the system you want to use it:
 export SLOTS=/sys/devices/bone_capemgr.9/slots  
 export PINS=/sys/kernel/debug/pinctrl/44e10800.pinmux/pins  
 echo PixBox > $SLOTS  
Of course, you can (and I believe you should) make this automatic by adding this into an executable script in /etc/profile.d.

Plugging the buttons and testing

Joysticks and buttons are basically the same thing: microswitches. They have two pins each, and the electric current passes when the user presses the button/direction.
If you have a look at my device tree overlay definition, you will notice that I set up my GPIOs with a pull-up resistor. Basically, this means that my GPIOs are, by default, in a "high" state (1). Connecting them to the ground sets them in "low" state (0).
All I had to do was linking one pin of my microswitches to the proper GPIO, the other to the ground, adding a "strong" resistor in-between.
I made my first experiments with a basic switch, a 10k resistor and a breadboard. Just plug one pin of your switch to your GPIO (say P8_8, "escape" button in my setup). The other one to the resistor, and the resistor to the ground of you board (P8_1 or P8_2).

For testing purposes, you will want to expose the GPIO as a special file. Go check the documents from Derek Molly, look for P8_8, and you will see that P8_8 is GPIO #67. Now, on your Beaglebone, write the value "67" to /sys/class/gpio/export and what the magic happen:
 root@beaglebone:~# cd /sys/class/gpio  
 root@beaglebone:/sys/class/gpio# ls  
 export gpiochip0 gpiochip32 gpiochip64 gpiochip96 unexport  
 root@beaglebone:/sys/class/gpio# echo 67 > export  
 root@beaglebone:/sys/class/gpio# ls  
 export gpio67 gpiochip0 gpiochip32 gpiochip64 gpiochip96 unexport  
After you write this value, a gpio67 folder appears. Check its "value" file:
 root@beaglebone:/sys/class/gpio# cat gpio67/value  
 1  
"1" means "high" state.
Toggle your switch, and check the value:
 root@beaglebone:/sys/class/gpio# cat gpio67/value  
 0  
Well done, you are one step closer to having your arcade controller!

From button events to keyboard events

In practice, we do not want to poll the status of the gpio[whatever]/value files. This does work, but is quite slow. We can do better than that! Actually, GPIO values are stored in specific addresses in memory, and we can access them programmatically.

Ethan Hayon (https://github.com/ehayon/BeagleBone-GPIO) did a great job providing C primitives to configure the GPIOs and access their values, and my implementation (https://github.com/alban-rochel/pixbox-controls/tree/master/service) largely relies on his code.

My code is basically polling the proper addresses, and emitting keyboard events when their states changes. To emit keyboard events, I used the uinput Linux subsystem, check it out! Very simple to use.

The only "smart" thing I did was adding a "debounce" period. "Bouncing" is what happens when you toggle the state of a switch: within a very short amount of time, the electric state may be unstable and oscillate between "high" and "low" values. You definitely don't want these to appear as button presses. So, after each state change (button press or depress), I ignore the button for a very short duration.

And that's it! Feel free to use this piece of code, all you have to do for your specific project is to change the "ADD_BUTTON" calls: the first argument is the key emitted, the second the GPIO, and the third one a debug string.

Actual building of the controller

I used the protocape from Adafruit to solder all my resistors and wires: not much to say about this, it is cheap and easy to use: https://www.adafruit.com/products/572

As for the arcade components, I ordered them on http://www.starcab.net/ . Standard 28mm buttons, Sanwa sticks, everything works (and feels) great!

Back to work!

It's been a long time since I have not updated this web page. One of the reasons is that motivation comes and goes. And the other reason is that following a disk crash, I have lost the file in which I had documented all my changes and the interesting links I had found...

I have been contacted recently by a reader of this blog, who made interesting improvements over my own researches - this re-ignited my motivation to continue this blog. I would be happy if he could provide more info through the commentary system.

As for the loss of my documentation on this subject. Well... I'll try and do my best!