Saturday, February 26, 2011

dreams of a mini MAME cabinet bring out my c++ chops

so I wanna build me a mini MAME cabinet. something along the lines of this. but i'm cheap, and i can't justify dropping $350 just to get my old-school street fighter turbo on. so i dug out an old linux laptop and got xmame running on it. surprisingly it seems to hold up pretty well. so i gotz the screen and brains part of the cabinet accounted for.

a'course, you can't have a MAME cabinet without a nice set of arcade controls (or two sets ). i'm not _too_ picky, so the x-arcade joystick and button parts will do just nicely. $20 per player is hard to beat.

and to interface all those nice buttons to the laptop there's the teensy++ usb microcontroller board. this little guy (among other cooler, more complex microcontrollery things) can take physical button presses and translate them into usb keyboard presses my little linux box can understand. and with the teensyduino add-on, i can program this thing with easy-to-use arduino environment. w00t!

there's only one problem: the Keyboard class that's provided can only do up to 6 simultaneous virtual keypresses, not enough if i'm gonna wire up two sets of controllers on this thing.

never to fear, my c++ skills are here. i dug in to the directory where teensyduino got installed and added a generic send method to the Keyboard class. here's the deets (i copied most of send_now() and made a few modifications) (unfortunatly, i don't have a teensy board yet, so i don't even know if this poo is gonna work. who want's to by ol' dick a birthday present? =]):

usb_api.h
class usb_keyboard_class : public Print
{
 public:
 virtual void write(uint8_t);
 void write_unicode(uint16_t unicode_code_point);
 void set_modifier(uint8_t);
 void set_key1(uint8_t);
 void set_key2(uint8_t);
 void set_key3(uint8_t);
 void set_key4(uint8_t);
 void set_key5(uint8_t);
 void set_key6(uint8_t);
 void send_now(void);
 // here's the method i added
 void send(uint8_t, uint8_t*, uint8_t);
 private:
 void write_keycode(KEYCODE_TYPE key);
 void write_key(uint8_t code);
 uint8_t utf8_state;
 uint16_t unicode_wchar;
};

usb_api.cpp
void usb_keyboard_class::send(uint8_t modifier_keys, uint8_t* keys, uint8_t n_keys)
{
        uint8_t i, intr_state, timeout;

        if (!usb_configuration) return;
        intr_state = SREG;
        cli();
        UENUM = KEYBOARD_ENDPOINT;
        timeout = UDFNUML + 50;
        while (1) {
                // are we ready to transmit?
                if (UEINTX & (1<<RWAL)) break;
                SREG = intr_state;
                // has the USB gone offline?
                if (!usb_configuration) return;
                // have we waited too long?
                if (UDFNUML == timeout) return;
                // get ready to try checking again
                intr_state = SREG;
                cli();
                UENUM = KEYBOARD_ENDPOINT;
        }
        UEDATX = modifier_keys;
        UEDATX = 0;
        for(i = 0; i < n_keys; i++){
            UEDATX = keys[i];
        }
        
        UEINTX = 0x3A;
        keyboard_idle_count = 0;
        SREG = intr_state;
}

here's what i got on how to use this shiny new method:

arcadestick.pde
/**
  * Compile with Teensduino
  * Board: Teensy++ 2.0
  * USB Type: Keyboard + Mouse
  */

#ifdef STR_PRODUCT
#  undef STR_PRODUCT
#endif
#define STR_PRODUCT L"DeathByAutoScroll Arcade Stick (X-Arcade Dual Compatible)"

#define MAX_SIMULTANEOUS_KEYS 25

uint8_t keys[MAX_SIMULTANEOUS_KEYS];
uint8_t modifier_keys;
uint8_t n_keys;

void setupPins(){
  // no need, all pins are defaulted to INPUT
}

void reset(){
  modifier_keys = 0;
  n_keys = 0;
}

void addKey(uint8_t key){
  keys[n_keys++] = key;
}

void addModifierKey(uint8_t key){
  modifier_keys |= key;
}

void setKeys(){
  // convert physical button presses to keyboard keys presses
  // follows x-arcdae dual-joystick keyboard mapping scheme
  // see http://www.xgaming.com/service/ServiceFiles/X-Arcade%20Tankstick%20Manual%20USA.pdf
  
  // player1 buttons on PORTD
  // player2 buttons on PORTC
  // player1 direction on lower nibble of PORTA
  // player2 direction on upper nibble of PORTA
  
  // player 1
  // direction
  //p1 up
  if(PIN_A0){
    addKey(KEYPAD_8);
  }
  //p1 down
  if(PIN_A1){
    addKey(KEYPAD_2);
  }
  //p1 left
  if(PIN_A2){
    addKey(KEYPAD_4);
  }
  //p1 right
  if(PIN_A3){
    addKey(KEYPAD_6);
  }
  // buttons
  //p1 1
  if(PIN_D0){
    addModifierKey(MODIFIERKEY_LEFT_CTRL);
  }
  //p1 2
  if(PIN_D1){
    addModifierKey(MODIFIERKEY_LEFT_ALT);
  }
  //p1 3
  if(PIN_D2){
    addKey(KEY_SPACE);
  }
  //p1 4
  if(PIN_D3){
    addModifierKey(MODIFIERKEY_LEFT_SHIFT);
  }
  //p1 5
  if(PIN_D4){
    addKey(KEY_Z);
  }
  //p1 6
  if(PIN_D5){
    addKey(KEY_X);
  }
  //p1 start
  if(PIN_D6){
    addKey(KEY_1);
  }
  //p1 coin
  if(PIN_D7){
    addKey(KEY_3);
  }
  
  // player 2
  // direction
  //p2 up
  if(PIN_A4){
    addKey(KEY_R);
  }
  //p2 down
  if(PIN_A5){
    addKey(KEY_F);
  }
  //p2 left
  if(PIN_A6){
    addKey(KEY_D);
  }
  //p2 right
  if(PIN_A7){
    addKey(KEY_G);
  }
  // buttons
  //p2 1
  if(PIN_C0){
    addKey(KEY_A);
  }
  //p2 2
  if(PIN_C1){
    addKey(KEY_S);
  }
  //p2 3
  if(PIN_C2){
    addKey(KEY_Q);
  }
  //p2 4
  if(PIN_C3){
    addKey(KEY_W);
  }
  //p2 5
  if(PIN_C4){
    addKey(KEY_E);
  }
  //p2 6
  if(PIN_C5){
    addKey(KEY_LEFT_BRACE);
  }
  //p2 start
  if(PIN_C6){
    addKey(KEY_2);
  }
  //p2 coin
  if(PIN_C7){
    addKey(KEY_4);
  }
}

void setup(){
  setupPins();
}

void loop(){
  reset();
  setKeys();
  Keyboard.send(modifier_keys, keys, n_keys);
}

now all i need is a sheet or two of MDF, a little cash for the parts, and a lot of free time to make it all happen.

Tuesday, January 18, 2011

muscling through a minor migraine while staring at a monitor

i woke up with a a headache this morning. what a way to start the day, eh? and it didn't let up when i finally arrived at the office, as late as i already was. what's worse is that any sort of light in my eyes made my head pound more. now, i can't afford to take the day off of work, or even just an extended morning break, i've got deadlines to meet.

here's how i muscled through that mildly malicious migraine:

step 1: treat the damn thing

everything you learned on how to treat a hangover is fair game here. this morning's recipe for success:
  • two tabs of tylenol (your preferred headache medicine will do)
  • get something to eat, the fattier the better (all i had was a bit of pound cake, but that's better than an empty stomach)
  • hydrate hydrate hydrate, and when you're done with that, hydrate some more
  • if you drink coffee, by all means chug that coffee!! you can kick that caffeine habit some other day

step 2: take it easy while still being productive

with a headache even thinking hurts, which is a problem if your job relies on brain activity. in my line of work, even the most menial of coding task takes some brain power. the solution: don't think. while you wait for the drugs-food-water-coffee concoction to kick in, do your busy-work tasks. answer some emails. fire off that email to HR that they've been houding you about. make some notes in your pivotal tracker issues. or hell, just catch up on some lifehacker posts. =]

step 3: turn down the lights

i don't know about you, but when that headache kicks in i just can't stand bright light. i'm lucky that the fluorescent tubes in my section of the office have been out for the past two weeks. i still have a big bright iMac monitor i have to stare into, though, and it seems like every application and every web page insists on having a bright white background color. never to fear, there's ways to tame it the beast:

  • start with your desktop background. you can go the quick-and-dirty route with simple all back, or go with any dark wallpaper of your choosing (my personal favorite is the cli cheat-sheet useful wallpaper).
  • any text editor worth its salt will let you change the font and background color scheme. Some MacOSX favorites: in Xcode head over to Preferences : Fonts and Colors, and go with the Midnight theme

    In TextMate try out the Twilight theme (no sparkly vampires here, sorry ladies). While you're at it, try out the Homebrew theme in Terminal
  • try out arc90's Readability bookmarklet with the Inverse style to tame website's colors, not to mention make pages more, well, readable with no more cruft and clutter around a page's actual content
  • if all else fails, invert your screen's colors. this option is usually buried in your computer's accessibility setting, sometimes called "high contrast" mode. If you're on a Mac you can go into inverted color mode with a ctrl-opt-command 8

Tuesday, January 11, 2011

how Dropbox saved my command line - extended

i came across a clever blog post on the 'tubes. it described how one could harness the file-syncing powers of Dropbox to keep one's bash shell config files all lined up across several machines. this was just the ticket to keep my mac laptop, my work imac, and my linux box command lines all in harmony with each other.


of course, i couldn't just leave well enough enough alone.


MacOSX hostname discrepancies

the first problem i ran into was reliably getting a machine's hostname on the MacOSX side. the network at the office insists on setting my imacs's hostname dynamically, and it's never the same between bootups. luckily i stumbled on a command that'll get the local hostname of a MacOSX computer:


Listing 1: getting the local hostname in MacOSX:

$ scutil --get LocalHostName


this will spit out whatever name you have set in System Preferences -> Sharing -> Computer Name, regardless of what the local networks says.


why not synced bin directories, too?

i have a handful of utility shell scripts that i had spread out across my different machines. similar to syncing up the config files, i came up with a way to consolidate all those scripts into a single shared ~/Dropbox/sh/bin directory. i also have a ~/Dropbox/sh/bin/Darwin directory for my MacOSX-specific scripts, and a ~/Dropbox/sh/bin/Linux for my Linux scripts, all of which my PATH environment variable appropriately points to. See Listing 2 to see it in action.


older Dropbox versions can't remember which scripts are executable

the official stable version of Dropbox on Linux maxes out at 0.7.x, which unfortunately doesn't sync the executable bit on files. that means all that effort getting my bin directories would be for naught. all is not lost! there exist Dropbox beta builds that go up to 1.0.10, which do support executable-bit syncing. the easiest way to get the latest dropbox beta builds for Linux is with a utility by the name of dbupdate. Note: if you have Dropbox installed for a single user (e.g. not system-wide, like how crunchbang linux does it by default) you'll have to run dbupdate manually from the command line. problem solved!


the goods

without further ado:


Listing 2: bashbootstrap:

# bashbootstrap
if [ -z "$PS1" ]; then
 return
fi

localhostname() {
 case $(uname) in
  "Darwin" )
  echo $(scutil --get LocalHostName)
  ;;
  "Linux" )
  echo $(hostname)
  ;;
  * )
  echo $HOSTNAME
  ;;
 esac
}

dropboxshelldir=~/Dropbox/sh
dropboxdir=$dropboxshelldir/etc

masterbashrc=$dropboxdir/bashrc
masterbashrcpost=$dropboxdir/bashrcpost
osbashrc=$masterbashrc-$(uname)
localbashrc=$osbashrc-$(localhostname)

masterbin=$dropboxshelldir/bin
osbin=$masterbin/$(uname)
localbin=$osbin/$(localhostname)

echo -n "Applicable shell configs: "
for bashfile in "$masterbashrc" "$osbashrc" "$localbashrc" "$masterbashrcpost"; do
 if [ -r $bashfile ]; then
  . $bashfile
  echo -n "$(basename $bashfile) "
 fi
done
echo

echo -n "Applicable bin directories: "
for bindir in "$masterbin" "$osbin" "$localbin"; do
 if [ -d $bindir ]; then
  PATH=$PATH:$bindir
  echo -n "$(basename $bindir) "
 fi
done
echo

# Set convenience aliases
myed=${VISUAL:-${EDITOR:-vim}}
alias editbashrc="$myed $masterbashrc"
alias editbashrcpost="$myed $masterbashrcpost"
alias editosbashrc="$myed $osbashrc"
alias editlocalbashrc="$myed $localbashrc"

a few odds-and-ends

  • MacOSX gets confused with .bash_profile and .bashrc, check bash_profile vs. bashrc for a solution
  • need some idea on how to spruce up you command line? lifehacker terminal tweaks is a good place to start
  • Don't have a Dropbox account yet? Why not use this dropbox referral link to get an extra 250MB of sync space gratis! (full disclosure: i'll get and extra 250MB of space, too =] )